From 541e7412e17468f88a33c916a3a2bc00e89b9611 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Thu, 30 Jul 2026 20:39:47 +0200 Subject: [PATCH 001/249] Bump version to 2026.8.0.dev0 --- src/pretix/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pretix/__init__.py b/src/pretix/__init__.py index 4e41c4a0a..4040a0967 100644 --- a/src/pretix/__init__.py +++ b/src/pretix/__init__.py @@ -19,4 +19,4 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -__version__ = "2026.7.0" +__version__ = "2026.8.0.dev0" -- 2.54.0 From 7627e4b54825318c87f2093bfa078b4d039fb85a Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Fri, 31 Jul 2026 16:56:43 +0200 Subject: [PATCH 002/249] Add Hungarian language --- src/pretix/_base_settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pretix/_base_settings.py b/src/pretix/_base_settings.py index 50d248386..f7d9c57b3 100644 --- a/src/pretix/_base_settings.py +++ b/src/pretix/_base_settings.py @@ -104,6 +104,7 @@ ALL_LANGUAGES = [ ('gl', _('Galician')), ('el', _('Greek')), ('he', _('Hebrew')), + ('hu', _('Hungarian')), ('id', _('Indonesian')), ('it', _('Italian')), ('ja', _('Japanese')), -- 2.54.0 From 01d736361d9bcc6fa1e98763047e5becde9953a1 Mon Sep 17 00:00:00 2001 From: pajowu Date: Mon, 3 Aug 2026 14:26:57 +0200 Subject: [PATCH 003/249] Cart Manager: Round custom price to currency when adding (#6426) --- src/pretix/base/services/cart.py | 5 +++ src/tests/presale/test_cart.py | 61 ++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/pretix/base/services/cart.py b/src/pretix/base/services/cart.py index c9ddf51a9..66eaa0b1f 100644 --- a/src/pretix/base/services/cart.py +++ b/src/pretix/base/services/cart.py @@ -53,6 +53,7 @@ from django.utils.translation import ( ) from django_scopes import scopes_disabled +from pretix.base.decimal import round_decimal from pretix.base.i18n import language from pretix.base.media import MEDIA_TYPES from pretix.base.models import ( @@ -916,6 +917,8 @@ class CartManager: if custom_price > 99_999_999_999: raise CartError(error_messages['price_too_high']) + custom_price = round_decimal(custom_price, currency=self.event.currency) + op = self.AddOperation( count=i['count'], item=item, @@ -1038,6 +1041,8 @@ class CartManager: if custom_price > 99_999_999_999: raise CartError(error_messages['price_too_high']) + custom_price = round_decimal(custom_price, currency=self.event.currency) + # Fix positions with wrong price (TODO: happens out-of-cartmanager-transaction and therefore a little hacky) for ca in current_addons[cp][a['item'], a['variation']]: if ca.listed_price != listed_price: diff --git a/src/tests/presale/test_cart.py b/src/tests/presale/test_cart.py index ab0a852fc..72eb9661f 100644 --- a/src/tests/presale/test_cart.py +++ b/src/tests/presale/test_cart.py @@ -697,6 +697,42 @@ class CartTest(CartTestMixin, TestCase): self.assertIsNone(objs[0].variation) self.assertEqual(objs[0].price, 23) + def test_free_price_rounding(self): + self.ticket.free_price = True + self.ticket.save() + + response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), { + 'item_%d' % self.ticket.id: '1', + 'price_%d' % self.ticket.id: '40.1234', + }, follow=True) + self.assertRedirects(response, '/%s/%s/?require_cookie=true' % (self.orga.slug, self.event.slug), + target_status_code=200) + + with scopes_disabled(): + cr1 = CartPosition.objects.get() + assert cr1.listed_price == Decimal('23.00') + assert cr1.custom_price_input == Decimal('40.12') + assert cr1.price == Decimal('40.12') + + def test_free_price_rounding_jpy(self): + self.event.currency = "JPY" + self.event.save() + self.ticket.free_price = True + self.ticket.save() + + response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), { + 'item_%d' % self.ticket.id: '1', + 'price_%d' % self.ticket.id: '40.1234', + }, follow=True) + self.assertRedirects(response, '/%s/%s/?require_cookie=true' % (self.orga.slug, self.event.slug), + target_status_code=200) + + with scopes_disabled(): + cr1 = CartPosition.objects.get() + assert cr1.listed_price == Decimal('23.00') + assert cr1.custom_price_input == Decimal('40.00') + assert cr1.price == Decimal('40.00') + def test_variation_inactive(self): self.shirt_red.active = False self.shirt_red.save() @@ -3060,6 +3096,31 @@ class CartAddonTest(CartTestMixin, TestCase): assert cp1.addons.count() == 3 assert all(a.price == Decimal('12.00') for a in cp1.addons.all()) + @classscope(attr='orga') + def test_free_price_rounding(self): + self.event.settings.locales = ['de'] + self.event.settings.locale = 'de' + self.event.currency = "JPY" + self.event.save() + + self.workshop1.free_price = True + self.workshop1.save() + cp1 = CartPosition.objects.create( + event=self.event, cart_id=self.session_key, item=self.ticket, + price=23, expires=now() - timedelta(minutes=10) + ) + + response = self.client.post('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), { + 'cp_{}_item_{}'.format(cp1.pk, self.workshop1.pk): '1', + 'cp_{}_item_{}_price'.format(cp1.pk, self.workshop1.pk): '99,99', + }, follow=True) + self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), + target_status_code=200) + with scopes_disabled(): + assert cp1.addons.count() == 1 + assert cp1.addons.first().item == self.workshop1 + assert cp1.addons.first().price == Decimal('100') + @classscope(attr='orga') def test_change_number(self): cp1 = CartPosition.objects.create( -- 2.54.0 From b70c1b02c2527cb69e90768e1f1188af1d91b4b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 08:46:25 +0200 Subject: [PATCH 004/249] Bump django-bootstrap3 from 26.1 to 26.2 (#6445) Bumps [django-bootstrap3](https://github.com/zostera/django-bootstrap3) from 26.1 to 26.2. - [Changelog](https://github.com/zostera/django-bootstrap3/blob/main/CHANGELOG.md) - [Commits](https://github.com/zostera/django-bootstrap3/compare/v26.1...v26.2) --- updated-dependencies: - dependency-name: django-bootstrap3 dependency-version: '26.2' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9d9b1469c..422d3f206 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ dependencies = [ "defusedcsv>=3.0.0", "dnspython==2.*", "Django[argon2]==5.2.*", - "django-bootstrap3==26.1", + "django-bootstrap3==26.2", "django-compressor==4.6.0", "django-countries==9.0.*", "django-filter==26.1", -- 2.54.0 From 2c876057bf22ec7a66dcf95c8fe5795461f7fa03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 08:46:52 +0200 Subject: [PATCH 005/249] Bump markdown from 3.10.2 to 3.10.3 (#6442) Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.10.2 to 3.10.3. - [Release notes](https://github.com/Python-Markdown/markdown/releases) - [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md) - [Commits](https://github.com/Python-Markdown/markdown/compare/3.10.2...3.10.3) --- updated-dependencies: - dependency-name: markdown dependency-version: 3.10.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 422d3f206..a2f6a455e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,7 +67,7 @@ dependencies = [ "kombu==5.6.*", "libsass==0.23.*", "lxml", - "markdown==3.10.2", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3. + "markdown==3.10.3", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3. # We can upgrade markdown again once django-bootstrap3 upgrades or once we drop Python 3.6 and 3.7 "mt-940==4.30.*", "oauthlib==3.3.*", -- 2.54.0 From c369ba5f60199ed2510940dab7dd49d1fc3e4bfe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 08:47:14 +0200 Subject: [PATCH 006/249] Update cryptography requirement from >=49.0.0 to >=50.0.0 (#6443) 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/49.0.0...50.0.0) --- updated-dependencies: - dependency-name: cryptography dependency-version: 50.0.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a2f6a455e..8a97db7cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "bleach==6.4.*", "celery==5.6.*", "chardet==5.2.*", - "cryptography>=49.0.0", + "cryptography>=50.0.0", "css-inline==0.21.*", "defusedcsv>=3.0.0", "dnspython==2.*", -- 2.54.0 From e4a0bba3bca24b1aba6cbabea1f8b7499ef6d122 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Tue, 4 Aug 2026 10:22:09 +0200 Subject: [PATCH 007/249] Fix font enumeration --- src/pretix/presale/style.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pretix/presale/style.py b/src/pretix/presale/style.py index 9945cbf6b..334372a6c 100644 --- a/src/pretix/presale/style.py +++ b/src/pretix/presale/style.py @@ -147,7 +147,7 @@ def get_font_stylesheet(font_name, organizer: Organizer = None, event: Event = N stylesheet = [] font = get_fonts(event)[font_name] for sty, formats in font.items(): - if sty == 'sample': + if sty in ['sample', 'pdf_only']: continue stylesheet.append('@font-face { ') stylesheet.append('font-family: "{}";'.format(font_name)) -- 2.54.0 From b20557a996496c92d408be45608819b0f07bbe70 Mon Sep 17 00:00:00 2001 From: pajowu Date: Tue, 4 Aug 2026 11:19:43 +0200 Subject: [PATCH 008/249] Show correct number of currency placed in widget (Z#23241522) (#6425) --- src/pretix/presale/views/widget.py | 1 + .../static/pretixpresale/js/widget/widget.js | 27 ++++++++++--------- .../static/pretixpresale/widget/src/api.ts | 1 + .../widget/src/components/Item.vue | 6 ++--- .../widget/src/components/PriceBox.vue | 10 +++---- .../pretixpresale/widget/src/sharedStore.ts | 3 +++ src/tests/presale/test_widget.py | 4 +++ 7 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/pretix/presale/views/widget.py b/src/pretix/presale/views/widget.py index a51af50a4..e0a28311b 100644 --- a/src/pretix/presale/views/widget.py +++ b/src/pretix/presale/views/widget.py @@ -795,6 +795,7 @@ class WidgetAPIProductList(EventListMixin, View): 'target_url': eventreverse_absolute(request.event, 'presale:event.index'), 'subevent': self.subevent.pk if self.subevent else None, 'currency': request.event.currency, + 'currency_places': settings.CURRENCY_PLACES.get(request.event.currency, 2), 'display_net_prices': request.event.settings.display_net_prices, 'use_native_spinners': request.event.settings.widget_use_native_spinners, 'show_variations_expanded': request.event.settings.show_variations_expanded, diff --git a/src/pretix/static/pretixpresale/js/widget/widget.js b/src/pretix/static/pretixpresale/js/widget/widget.js index 3529acdd8..22c44c8ed 100644 --- a/src/pretix/static/pretixpresale/js/widget/widget.js +++ b/src/pretix/static/pretixpresale/js/widget/widget.js @@ -380,16 +380,16 @@ Vue.component('pricebox', { }, display_price: function () { if (this.$root.display_net_prices) { - return floatformat(parseFloat(this.price.net), 2); + return floatformat(this.price.net, this.$root.currency_places); } else { - return floatformat(parseFloat(this.price.gross), 2); + return floatformat(this.price.gross, this.$root.currency_places); } }, display_price_nonlocalized: function () { if (this.$root.display_net_prices) { - return parseFloat(this.price.net).toFixed(2); + return parseFloat(this.price.net).toFixed(this.$root.currency_places); } else { - return parseFloat(this.price.gross).toFixed(2); + return parseFloat(this.price.gross).toFixed(this.$root.currency_places); } }, suggested_price_nonlocalized: function () { @@ -398,9 +398,9 @@ Vue.component('pricebox', { price = this.price; } if (this.$root.display_net_prices) { - return parseFloat(price.net).toFixed(2); + return parseFloat(price.net).toFixed(this.$root.currency_places); } else { - return parseFloat(price.gross).toFixed(2); + return parseFloat(price.gross).toFixed(this.$root.currency_places); } }, original_price_aria_label: function () { @@ -410,7 +410,7 @@ Vue.component('pricebox', { return django.interpolate(strings.new_price, [this.stripHTML(this.priceline)]); }, original_line: function () { - return '' + this.$root.currency + " " + floatformat(parseFloat(this.original_price), 2); + return '' + this.$root.currency + " " + floatformat(this.original_price, this.$root.currency_places); }, priceline: function () { if (this.price.gross === "0.00") { @@ -645,19 +645,19 @@ Vue.component('item', { if (this.item.free_price) { return django.interpolate(strings.price_from, { 'currency': this.$root.currency, - 'price': floatformat(this.item.min_price, 2) + 'price': floatformat(this.item.min_price, this.$root.currency_places) }, true).replace(this.$root.currency, '' + this.$root.currency + ''); } else if (this.item.min_price !== this.item.max_price) { - return '' + this.$root.currency + " " - + floatformat(this.item.min_price, 2) + " – " - + floatformat(this.item.max_price, 2); + return '' + this.$root.currency + " " + + floatformat(this.item.min_price, this.$root.currency_places) + " – " + + floatformat(this.item.max_price, this.$root.currency_places); } else if (this.item.min_price === "0.00" && this.item.max_price === "0.00") { if (this.item.mandatory_priced_addons) { return "\xA0"; // nbsp, because an empty string would cause the HTML element to collapse } return strings.free; } else { - return '' + this.$root.currency + " " + floatformat(this.item.min_price, 2); + return '' + this.$root.currency + " " + floatformat(this.item.min_price, this.$root.currency_places); } }, variationsToggleLabel: function () { @@ -1949,6 +1949,7 @@ var shared_root_methods = { root.location = data.location; root.categories = data.items_by_category; root.currency = data.currency; + root.currency_places = data.currency_places; root.display_net_prices = data.display_net_prices; root.voucher_explanation_text = data.voucher_explanation_text; root.error = data.error; @@ -1983,6 +1984,7 @@ var shared_root_methods = { }, function (error) { root.categories = []; root.currency = ''; + root.currency_places = 2; if (error.status === 429) { root.error = strings['loading_error_429']; root.connection_error = true; @@ -2339,6 +2341,7 @@ var create_widget = function (element, html_id=null) { is_button: false, categories: null, currency: null, + currency_places: 2, name: null, date_range: null, location: null, diff --git a/src/pretix/static/pretixpresale/widget/src/api.ts b/src/pretix/static/pretixpresale/widget/src/api.ts index 23650186c..cbe0a9e19 100644 --- a/src/pretix/static/pretixpresale/widget/src/api.ts +++ b/src/pretix/static/pretixpresale/widget/src/api.ts @@ -22,6 +22,7 @@ export interface ProductListResponse { location?: string items_by_category?: Category[] currency?: string + currency_places?: number display_net_prices?: boolean voucher_explanation_text?: string error?: string diff --git a/src/pretix/static/pretixpresale/widget/src/components/Item.vue b/src/pretix/static/pretixpresale/widget/src/components/Item.vue index 260ee6987..d2c115a57 100644 --- a/src/pretix/static/pretixpresale/widget/src/components/Item.vue +++ b/src/pretix/static/pretixpresale/widget/src/components/Item.vue @@ -72,7 +72,7 @@ const pricerange = computed(() => { STRINGS.price_from, { currency: store.currency, - price: floatformat(props.item.min_price || '0', 2), + price: floatformat(props.item.min_price || '0', store.currency_places), }, true ).replace( @@ -80,14 +80,14 @@ const pricerange = computed(() => { `${store.currency}` ) } else if (props.item.min_price !== props.item.max_price) { - return `${store.currency} ${floatformat(props.item.min_price || '0', 2)} – ${floatformat(props.item.max_price || '0', 2)}` + return `${store.currency} ${floatformat(props.item.min_price || '0', store.currency_places)} – ${floatformat(props.item.max_price || '0', store.currency_places)}` } else if (props.item.min_price === '0.00' && props.item.max_price === '0.00') { if (props.item.mandatory_priced_addons) { return '\xA0' // nbsp, because an empty string would cause the HTML element to collapse } return STRINGS.free } else { - return `${store.currency} ${floatformat(props.item.min_price || '0', 2)}` + return `${store.currency} ${floatformat(props.item.min_price || '0', store.currency_places)}` } }) diff --git a/src/pretix/static/pretixpresale/widget/src/components/PriceBox.vue b/src/pretix/static/pretixpresale/widget/src/components/PriceBox.vue index 3ce01162c..858c5c991 100644 --- a/src/pretix/static/pretixpresale/widget/src/components/PriceBox.vue +++ b/src/pretix/static/pretixpresale/widget/src/components/PriceBox.vue @@ -25,9 +25,9 @@ const ariaLabelledby = computed(() => `${store.htmlId}-item-label-${props.itemId const displayPrice = computed(() => { if (store.displayNetPrices) { - return floatformat(parseFloat(props.price.net), 2) + return floatformat(props.price.net, store.currency_places) } - return floatformat(parseFloat(props.price.gross), 2) + return floatformat(props.price.gross, store.currency_places) }) const displayPriceNonlocalized = computed(() => { @@ -40,15 +40,15 @@ const displayPriceNonlocalized = computed(() => { const suggestedPriceNonlocalized = computed(() => { const price = props.suggestedPrice ?? props.price if (store.displayNetPrices) { - return parseFloat(price.net).toFixed(2) + return parseFloat(price.net).toFixed(store.currency_places) } - return parseFloat(price.gross).toFixed(2) + return parseFloat(price.gross).toFixed(store.currency_places) }) // TODO BAD const originalLine = computed(() => { if (!props.originalPrice) return '' - return `${store.currency} ${floatformat(parseFloat(props.originalPrice), 2)}` + return `${store.currency} ${floatformat(props.originalPrice, store.currency_places)}` }) // TODO BAD diff --git a/src/pretix/static/pretixpresale/widget/src/sharedStore.ts b/src/pretix/static/pretixpresale/widget/src/sharedStore.ts index f43398316..00d36c02f 100644 --- a/src/pretix/static/pretixpresale/widget/src/sharedStore.ts +++ b/src/pretix/static/pretixpresale/widget/src/sharedStore.ts @@ -71,6 +71,7 @@ export function createWidgetStore (config: { frontpageText: null as string | null, categories: [] as Category[], currency: '', + currency_places: 2, displayNetPrices: false, voucherExplanationText: null as string | null, displayAddToCart: false, @@ -299,6 +300,7 @@ export function createWidgetStore (config: { this.location = data.location ?? null this.categories = data.items_by_category ?? [] this.currency = data.currency ?? '' + this.currency_places = data.currency_places ?? 2 this.displayNetPrices = data.display_net_prices ?? false this.voucherExplanationText = data.voucher_explanation_text ?? null this.error = data.error ?? null @@ -338,6 +340,7 @@ export function createWidgetStore (config: { } catch (e) { this.categories = [] this.currency = '' + this.currency_places = 2 if (e instanceof ApiError && e.status === 429) { this.error = STRINGS.loading_error_429 } else { diff --git a/src/tests/presale/test_widget.py b/src/tests/presale/test_widget.py index 10795cb15..0738326d7 100644 --- a/src/tests/presale/test_widget.py +++ b/src/tests/presale/test_widget.py @@ -173,6 +173,7 @@ class WidgetCartTest(CartTestMixin, TestCase): "frontpage_text": "", "location": "", "currency": "EUR", + "currency_places": 2, "show_variations_expanded": False, "display_net_prices": False, "use_native_spinners": False, @@ -379,6 +380,7 @@ class WidgetCartTest(CartTestMixin, TestCase): "frontpage_text": "", "location": "", "currency": "EUR", + "currency_places": 2, "show_variations_expanded": False, "display_net_prices": False, "use_native_spinners": False, @@ -439,6 +441,7 @@ class WidgetCartTest(CartTestMixin, TestCase): "frontpage_text": "", "location": "", "currency": "EUR", + "currency_places": 2, "show_variations_expanded": False, "display_net_prices": False, "use_native_spinners": False, @@ -524,6 +527,7 @@ class WidgetCartTest(CartTestMixin, TestCase): "frontpage_text": "", "location": "", "currency": "EUR", + "currency_places": 2, 'poweredby': 'ticketing powered by pretix', "show_variations_expanded": False, "display_net_prices": False, -- 2.54.0 From c11ebb139138709a7b1171ffd2118228df7882a1 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Tue, 4 Aug 2026 22:59:19 +0200 Subject: [PATCH 009/249] Skip over Exporters with ExportError-Exceptions (such as ORMQL-Exporter) (Fixes PRETIXEU-F3X) --- src/pretix/api/views/exporters.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/pretix/api/views/exporters.py b/src/pretix/api/views/exporters.py index 66b5f1010..c48b5dd72 100644 --- a/src/pretix/api/views/exporters.py +++ b/src/pretix/api/views/exporters.py @@ -45,7 +45,7 @@ from pretix.base.models import ( ) from pretix.base.models.organizer import TeamQuerySet from pretix.base.services.export import ( - export, init_event_exporters, init_organizer_exporters, multiexport, + export, init_event_exporters, init_organizer_exporters, multiexport, ExportError, ) from pretix.helpers.http import ChunkBasedFileResponse @@ -149,8 +149,11 @@ class EventExportersViewSet(ExportersMixin, viewsets.ViewSet): )) exporters = [] for ex in sorted(raw_exporters, key=lambda ex: str(ex.verbose_name)): - ex._serializer = JobRunSerializer(exporter=ex) - exporters.append(ex) + try: + ex._serializer = JobRunSerializer(exporter=ex) + exporters.append(ex) + except ExportError: + pass return exporters def do_export(self, cf, instance, data): @@ -180,8 +183,11 @@ class OrganizerExportersViewSet(ExportersMixin, viewsets.ViewSet): )) exporters = [] for ex in sorted(raw_exporters, key=lambda ex: str(ex.verbose_name)): - ex._serializer = JobRunSerializer(exporter=ex) - exporters.append(ex) + try: + ex._serializer = JobRunSerializer(exporter=ex) + exporters.append(ex) + except ExportError: + pass return exporters def do_export(self, cf, instance, data): -- 2.54.0 From b6595347723edf85a20ca316e762025890320e6d Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Tue, 4 Aug 2026 23:01:29 +0200 Subject: [PATCH 010/249] isort --- src/pretix/api/views/exporters.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pretix/api/views/exporters.py b/src/pretix/api/views/exporters.py index c48b5dd72..26aca588f 100644 --- a/src/pretix/api/views/exporters.py +++ b/src/pretix/api/views/exporters.py @@ -45,7 +45,8 @@ from pretix.base.models import ( ) from pretix.base.models.organizer import TeamQuerySet from pretix.base.services.export import ( - export, init_event_exporters, init_organizer_exporters, multiexport, ExportError, + ExportError, export, init_event_exporters, init_organizer_exporters, + multiexport, ) from pretix.helpers.http import ChunkBasedFileResponse -- 2.54.0 From e7657a3dd3986b41cf0a0e5968cc603b9d6ae6d8 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 5 Aug 2026 10:56:46 +0200 Subject: [PATCH 011/249] Prevent HTML injection in email preview (Z#23241741) --- src/pretix/base/services/placeholders.py | 14 +++++++++++--- src/pretix/control/views/organizer.py | 16 ++++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/pretix/base/services/placeholders.py b/src/pretix/base/services/placeholders.py index 1776b25e0..4c84b16ef 100644 --- a/src/pretix/base/services/placeholders.py +++ b/src/pretix/base/services/placeholders.py @@ -801,11 +801,10 @@ def get_available_placeholders(event, base_parameters, rich=False): return params -def get_sample_context(event, context_parameters, rich=True): +def prepare_sample_context_for_preview(placeholder_to_sample): context_dict = {} lbl = _('This value will be replaced based on dynamic parameters.') - for k, v in get_available_placeholders(event, context_parameters, rich=rich).items(): - sample = v.render_sample(event) + for k, sample in placeholder_to_sample.items(): if isinstance(sample, PlainHtmlAlternativeString): context_dict[k] = PlainHtmlAlternativeString( '<{el} class="placeholder" title="{title}">{plain}'.format( @@ -830,3 +829,12 @@ def get_sample_context(event, context_parameters, rich=True): escape(sample) )) return context_dict + + +def get_sample_context(event, context_parameters, rich=True): + return prepare_sample_context_for_preview( + { + k: v.render_sample(event) + for k, v in get_available_placeholders(event, context_parameters, rich=rich).items() + } + ) diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index e5de190c0..ca6d5f11f 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -108,6 +108,7 @@ from pretix.base.services.export import ( init_organizer_exporters, multiexport, scheduled_organizer_export, ) from pretix.base.services.mail import mail, prefix_subject +from pretix.base.services.placeholders import prepare_sample_context_for_preview from pretix.base.templatetags.rich_text import markdown_compile_email from pretix.base.views.tasks import AsyncAction from pretix.control.forms.exports import ScheduledOrganizerExportForm @@ -345,16 +346,11 @@ class MailSettingsPreview(OrganizerPermissionRequiredMixin, View): # get all supported placeholders with dummy values def placeholders(self, item): - ctx = {} - for p, s in MailSettingsForm(obj=self.request.organizer)._get_sample_context( - MailSettingsForm.base_context[item]).items(): - if s.strip().startswith('*'): - ctx[p] = s - else: - ctx[p] = '{}'.format( - _('This value will be replaced based on dynamic parameters.'), - s - ) + ctx = prepare_sample_context_for_preview( + MailSettingsForm(obj=self.request.organizer)._get_sample_context( + MailSettingsForm.base_context[item] + ) + ) return self.SafeDict(ctx) def post(self, request, *args, **kwargs): -- 2.54.0 From 8133061fe1b21696c073c6dcd1b6b9f790219ec9 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 5 Aug 2026 10:57:13 +0200 Subject: [PATCH 012/249] Devices: Store timestamp of last contact (#6453) --- src/pretix/api/auth/device.py | 26 ++++++++++- .../base/migrations/0307_devicelastseen.py | 43 +++++++++++++++++++ src/pretix/base/models/devices.py | 20 +++++++++ src/pretix/helpers/database.py | 19 ++++++++ src/tests/api/test_deviceauth.py | 26 +++++++++++ 5 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/pretix/base/migrations/0307_devicelastseen.py diff --git a/src/pretix/api/auth/device.py b/src/pretix/api/auth/device.py index 00fc03ba0..63fd1892c 100644 --- a/src/pretix/api/auth/device.py +++ b/src/pretix/api/auth/device.py @@ -20,8 +20,11 @@ # . # import logging +from datetime import timedelta from django.contrib.auth.models import AnonymousUser +from django.db import DatabaseError +from django.utils.timezone import now from django_scopes import scopes_disabled from rest_framework import exceptions from rest_framework.authentication import TokenAuthentication @@ -30,6 +33,7 @@ from pretix.api.auth.devicesecurity import ( FullAccessSecurityProfile, get_all_security_profiles, ) from pretix.base.models import Device +from pretix.base.models.devices import DeviceLastSeen logger = logging.getLogger(__name__) @@ -42,7 +46,7 @@ class DeviceTokenAuthentication(TokenAuthentication): model = self.get_model() try: with scopes_disabled(): - device = model.objects.select_related('organizer').get(api_token=key) + device = model.objects.select_related('organizer', 'last_seen').get(api_token=key) except model.DoesNotExist: raise exceptions.AuthenticationFailed('Invalid token.') @@ -53,6 +57,7 @@ class DeviceTokenAuthentication(TokenAuthentication): logging.warning(f'Connection attempt of revoked device {device.pk}.') raise exceptions.AuthenticationFailed('Device access has been revoked.') + self._update_last_seen(device) return AnonymousUser(), device def authenticate(self, request): @@ -63,3 +68,22 @@ class DeviceTokenAuthentication(TokenAuthentication): if not profile.is_allowed(request): raise exceptions.PermissionDenied('Request denied by device security profile.') return r + + def _update_last_seen(self, device: Device): + try: + try: + last_seen_obj = device.last_seen + except DeviceLastSeen.DoesNotExist: + # First request from device, create model, ignore result. Use get_or_create to be safe + # against concurrent create requests + DeviceLastSeen.objects.get_or_create(device=device, last_seen=now()) + else: + if now() - last_seen_obj.last_seen < timedelta(seconds=10): + # We don't need to know the last seen info of a device to more precision than this, + # so we can avoid some database writes if the device is bursting a lot of requests. + return + last_seen_obj.last_seen = now() + last_seen_obj.save(update_fields=["last_seen"]) + except DatabaseError: + # Do not stop the request from happening + logger.exception("Database error while updating last_seen") diff --git a/src/pretix/base/migrations/0307_devicelastseen.py b/src/pretix/base/migrations/0307_devicelastseen.py new file mode 100644 index 000000000..647055cb5 --- /dev/null +++ b/src/pretix/base/migrations/0307_devicelastseen.py @@ -0,0 +1,43 @@ +# Generated by Django 5.2.16 on 2026-08-05 08:00 + +import django.db.models.deletion +from django.db import migrations, models + +import pretix.helpers.database + + +class Migration(migrations.Migration): + + dependencies = [ + ("pretixbase", "0306_alter_eventmetaproperty_unique_together"), + ] + + operations = [ + migrations.CreateModel( + name="DeviceLastSeen", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False + ), + ), + ("last_seen", models.DateTimeField(auto_now=True)), + ( + "device", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to="pretixbase.device", + ), + ), + ], + ), + migrations.AddIndex( + model_name="devicelastseen", + index=pretix.helpers.database.BrinIndexIgnoredOnSQLite( + models.F("last_seen"), + autosummarize=True, + name="pretixbase_device_last_seen", + ), + ), + ] diff --git a/src/pretix/base/models/devices.py b/src/pretix/base/models/devices.py index 726586e8e..8997da3fe 100644 --- a/src/pretix/base/models/devices.py +++ b/src/pretix/base/models/devices.py @@ -32,6 +32,7 @@ from pretix.base.models import LoggedModel from pretix.base.permissions import ( AnyPermissionOf, assert_valid_event_permission, ) +from pretix.helpers import BrinIndexIgnoredOnSQLite @scopes_disabled() @@ -287,3 +288,22 @@ class Device(LoggedModel): return self.get_events_with_any_permission() else: return self.organizer.events.none() + + +class DeviceLastSeen(models.Model): + # This is a separate model since we expect it to get A LOT of writes and PostgreSQL always + # writes full rows and then needs to update all indexes on the row, so this is going to save a + # lot of write traffic on the databse + device = models.OneToOneField("Device", on_delete=models.CASCADE, related_name="last_seen") + last_seen = models.DateTimeField(auto_now=True) + + class Meta: + indexes = [ + BrinIndexIgnoredOnSQLite( + # BRIN indexes are highly efficient on lots of updates, especially of chronological data + # and especially if we later want to query them by range, as we likely want to. + "last_seen", + name="pretixbase_device_last_seen", + autosummarize=True + ) + ] diff --git a/src/pretix/helpers/database.py b/src/pretix/helpers/database.py index d9d8ffb16..06360cb05 100644 --- a/src/pretix/helpers/database.py +++ b/src/pretix/helpers/database.py @@ -22,6 +22,7 @@ import contextlib from django.conf import settings +from django.contrib.postgres.indexes import BrinIndex from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured from django.db import connection, transaction from django.db.models import ( @@ -285,3 +286,21 @@ def get_deterministic_ordering(model, ordering): # on the primary key to provide total ordering. ordering.append("-pk") return ordering + + +class IgnoreOnSQLiteMixin: + # Mixin to allow defining PostgreSQL-specific indexes that will just not be created + # on SQLite. SQLite is supported for testing only anyways! + def create_sql(self, model, schema_editor, *args, **kwargs): + if "sqlite" in settings.DATABASES["default"]["ENGINE"]: + return "" + return super().create_sql(model, schema_editor, *args, **kwargs) + + def remove_sql(self, model, schema_editor, **kwargs): + if "sqlite" in settings.DATABASES["default"]["ENGINE"]: + return "" + return super().remove_sql(model, schema_editor, **kwargs) + + +class BrinIndexIgnoredOnSQLite(IgnoreOnSQLiteMixin, BrinIndex): + pass diff --git a/src/tests/api/test_deviceauth.py b/src/tests/api/test_deviceauth.py index dd29809b7..e1f29c2fb 100644 --- a/src/tests/api/test_deviceauth.py +++ b/src/tests/api/test_deviceauth.py @@ -20,13 +20,16 @@ # . # import base64 +from datetime import datetime, timezone import pytest from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.serialization import load_pem_private_key from django_scopes import scopes_disabled +from freezegun import freeze_time from pretix.base.models import Device +from pretix.base.models.devices import DeviceLastSeen @pytest.fixture @@ -386,3 +389,26 @@ def test_device_info_key_sets(device_client, device: Device): base64.b64decode(ks['diversification_key']), padding.PKCS1v15() ) + + +@pytest.mark.django_db +def test_update_last_seen(device_client, device: Device): + assert not DeviceLastSeen.objects.exists() + + with freeze_time("2020-01-10T14:30:00+00:00"): + resp = device_client.get('/api/v1/device/info') + assert resp.status_code == 200 + assert device.last_seen.last_seen == datetime(2020, 1, 10, 14, 30, tzinfo=timezone.utc) + + with freeze_time("2020-01-10T14:30:05+00:00"): + resp = device_client.get('/api/v1/device/info') + assert resp.status_code == 200 + # No update, interal too short + device.last_seen.refresh_from_db() + assert device.last_seen.last_seen == datetime(2020, 1, 10, 14, 30, tzinfo=timezone.utc) + + with freeze_time("2020-01-10T14:30:30+00:00"): + resp = device_client.get('/api/v1/device/info') + assert resp.status_code == 200 + device.last_seen.refresh_from_db() + assert device.last_seen.last_seen == datetime(2020, 1, 10, 14, 30, 30, tzinfo=timezone.utc) -- 2.54.0 From 9fe25544c17cd49039ec55b858c38520bb8b7413 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 5 Aug 2026 11:04:56 +0200 Subject: [PATCH 013/249] Email settings: Fix DMARC warning (#6452) --- src/pretix/control/views/mailsetup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pretix/control/views/mailsetup.py b/src/pretix/control/views/mailsetup.py index 63abf3388..075fd6235 100644 --- a/src/pretix/control/views/mailsetup.py +++ b/src/pretix/control/views/mailsetup.py @@ -269,8 +269,8 @@ class MailSettingsSetupView(TemplateView): if settings.MAIL_CUSTOM_SENDER_DMARC_REQUIRED: dmarc_record = get_dmarc_record(hostname) if not dmarc_record: - spf_warning = _( - 'We did not find DMARC record for your domain. This means that there is a very high chance ' + dmarc_warning = _( + 'We did not find a DMARC record for your domain. This means that there is a very high chance ' 'most of the emails will be rejected or marked as spam. You should update the DNS settings ' 'of your domain.' ) -- 2.54.0 From 407728cc5500cec225c9b4624f5bf07442c87dbb Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 5 Aug 2026 11:46:52 +0200 Subject: [PATCH 014/249] Bulk refund: Fix only first refund being made (Z#23239965) (#6376) --- src/pretix/control/views/orders.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py index e1c19c549..fec211335 100644 --- a/src/pretix/control/views/orders.py +++ b/src/pretix/control/views/orders.py @@ -382,7 +382,7 @@ class OrderOverpaidRefundBulkActionView(BaseOrderBulkActionView): 'provider': refund.provider, }, user=self.request.user) payment.payment_provider.execute_refund(refund) - return True + return bool(proposals) except (ValueError, PaymentException): return False -- 2.54.0 From c4950e714dfa9900ad3692fcd47b2d812e411b31 Mon Sep 17 00:00:00 2001 From: Nikolai Date: Fri, 31 Jul 2026 11:41:45 +0200 Subject: [PATCH 015/249] Translations: Update Danish Currently translated at 62.7% (4011 of 6387 strings) Translation: pretix/pretix Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/da/ powered by weblate --- src/pretix/locale/da/LC_MESSAGES/django.po | 344 ++++++++------------- 1 file changed, 133 insertions(+), 211 deletions(-) diff --git a/src/pretix/locale/da/LC_MESSAGES/django.po b/src/pretix/locale/da/LC_MESSAGES/django.po index ca0577bb1..64de4f21e 100644 --- a/src/pretix/locale/da/LC_MESSAGES/django.po +++ b/src/pretix/locale/da/LC_MESSAGES/django.po @@ -4,10 +4,10 @@ msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-30 13:52+0000\n" -"PO-Revision-Date: 2026-07-27 17:00+0000\n" +"PO-Revision-Date: 2026-07-31 18:00+0000\n" "Last-Translator: Nikolai \n" -"Language-Team: Danish \n" +"Language-Team: Danish \n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1381,10 +1381,8 @@ msgid "Membership type" msgstr "Medlemskabstype" #: pretix/base/exporters/customers.py -#, fuzzy -#| msgid "Purchase time" msgid "Purchase ticket" -msgstr "Købsdato" +msgstr "Køb billet" #: pretix/base/exporters/customers.py pretix/base/exporters/orderlist.py #: pretix/base/exporters/waitinglist.py pretix/base/forms/questions.py @@ -1401,10 +1399,8 @@ msgid "Start date" msgstr "Starttidspunkt" #: pretix/base/exporters/customers.py -#, fuzzy -#| msgid "Start time from" msgid "Start time" -msgstr "Starttidspunkt fra" +msgstr "Starttidspunkt" #: pretix/base/exporters/customers.py pretix/base/exporters/orderlist.py #: pretix/base/exporters/waitinglist.py pretix/base/models/memberships.py @@ -1417,10 +1413,8 @@ msgid "End date" msgstr "Sluttidspunkt" #: pretix/base/exporters/customers.py -#, fuzzy -#| msgid "End: %(time)s" msgid "End time" -msgstr "Slut: %(time)s" +msgstr "Sluttidspunkt" #: pretix/base/exporters/dekodi.py pretix/base/exporters/invoices.py msgctxt "export_category" @@ -4641,16 +4635,12 @@ msgid "This event is remote or partially remote." msgstr "Dette arrangement afholdes online eller delvist online." #: pretix/base/models/event.py -#, fuzzy -#| msgid "" -#| "This will be used to let users know if the event is in a different " -#| "timezone and let’s us calculate users’ local times." msgid "" "This will be used to let users know if the event is in a different timezone, " "and to let us calculate the local time of a user." msgstr "" "Dette vil blive brugt til at informere brugerne om, hvorvidt begivenheden " -"finder sted i en anden tidszone, og til at beregne brugernes lokale tid." +"finder sted i en anden tidszone, og til at beregne brugerens lokale tid." #: pretix/base/models/event.py pretix/base/models/organizer.py #: pretix/control/navigation.py @@ -5472,14 +5462,6 @@ msgid "Reusable media policy" msgstr "Politik for genanvendelige medier" #: pretix/base/models/items.py -#, fuzzy -#| msgid "" -#| "If this product should be stored on a re-usable physical medium, you can " -#| "attach a physical media policy. This is not required for regular tickets, " -#| "which just use a one-time barcode, but only for products like renewable " -#| "season tickets or re-chargeable gift card wristbands. This is an advanced " -#| "feature that also requires specific configuration of ticketing and " -#| "printing settings." msgid "" "If this product should be stored on a reusable physical medium, you can " "attach a physical media policy. This is not required for regular tickets, " @@ -5488,12 +5470,12 @@ msgid "" "feature that also requires specific configuration of ticketing and printing " "settings." msgstr "" -"Hvis dette produkt skal opbevares på et genanvendeligt fysisk medie, kan du " -"vedhæfte en politik for fysiske medier. Dette er ikke nødvendigt for " -"almindelige billetter, som bare bruger en engangs-stregkode, men kun for " -"produkter som f.eks. fornyelige sæsonkort eller genopladelige gavekort-" -"armbånd. Dette er en avanceret funktion, som også kræver en specifik " -"konfiguration af billet- og printindstillinger." +"Hvis dette produkt skal opbevares på et genanvendeligt fysisk medium, kan du " +"knytte en politik for fysiske medier til det. Dette er ikke påkrævet for " +"almindelige billetter, som blot bruger en engangsstregkode, men gælder kun " +"for produkter som sæsonkort, der kan fornyes, eller gavekortarmbånd, som kan " +"blive genopladet. Dette er en avanceret funktion, der også kræver en " +"specifik konfiguration af billet- og udskrivningsindstillingerne." #: pretix/base/models/items.py msgid "Reusable media type" @@ -5537,6 +5519,9 @@ msgid "" "prior to their usage. Therefore, the selected media policy does not make " "sense for this media type." msgstr "" +"Den valgte medietype kræver, at alle medier registreres i systemet, inden de " +"tages i brug. Derfor giver den valgte mediepolitik ikke mening for denne " +"medietype." #: pretix/base/models/items.py msgid "" @@ -6090,18 +6075,16 @@ msgstr "afvist" #: pretix/base/models/media.py msgctxt "reusable_medium" msgid "Claim token" -msgstr "" +msgstr "Hent token" #: pretix/base/models/media.py msgctxt "reusable_medium" msgid "Label" -msgstr "" +msgstr "Etiket" #: pretix/base/models/media.py -#, fuzzy -#| msgid "Linked ticket" msgid "Linked tickets" -msgstr "Forbundet billet" +msgstr "Sammenkædede billetter" #: pretix/base/models/media.py msgid "" @@ -6109,6 +6092,9 @@ msgid "" "validity. If multiple tickets are valid at once, this will lead to failed " "check-ins." msgstr "" +"Hvis du linker til mere end én billet, skal du sikre dig, at der ikke er " +"nogen overlapning i gyldighedsperioden. Hvis flere billetter er gyldige på " +"samme tid, vil det medføre, at check-in mislykkes." #: pretix/base/models/memberships.py #: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html @@ -7568,6 +7554,8 @@ msgid "" "This payment provider exists for historical purposes only and is no longer " "usable." msgstr "" +"Denne betalingsudbyder findes udelukkende af historiske årsager og kan ikke " +"længere benyttes." #: pretix/base/pdf.py msgid "Ticket code (barcode content)" @@ -7802,15 +7790,12 @@ msgid "Atlantis" msgstr "Eksempelland" #: pretix/base/pdf.py -#, fuzzy msgid "Invoice custom recipient field" -msgstr "Fakturamodtager:" +msgstr "Brugerdefineret modtagerfelt på fakturaen" #: pretix/base/pdf.py -#, fuzzy -#| msgid "Custom recipient field label" msgid "Custom recipient field" -msgstr "Brugerdefineret etiket til modtagerfelt" +msgstr "Brugerdefineret modtagerfelt" #: pretix/base/pdf.py msgid "List of Add-Ons" @@ -8252,7 +8237,7 @@ msgstr "Arrangement aflyst" #: pretix/base/services/cancelevent.py msgid "Confirm event cancellation and bulk refund" -msgstr "" +msgstr "Bekræft aflysning af begivenhed og samlet refusion" #: pretix/base/services/cart.py pretix/base/services/modelimport.py #: pretix/base/services/orders.py @@ -8781,13 +8766,15 @@ msgstr "Du skal svare på spørgsmål for at gennemføre dette tjek-ind." #: pretix/base/services/checkin.py msgid "Ticket needs to be exchanged to a suitable medium." -msgstr "" +msgstr "Billetten skal ombyttes til et passende medium." #: pretix/base/services/checkin.py msgid "" "This ticket has already been exchanged for a reusable medium that now needs " "to be used instead." msgstr "" +"Denne billet er allerede blevet ombyttet til et genanvendeligt medium, som " +"nu skal bruges i stedet." #: pretix/base/services/checkin.py msgid "This ticket has already been redeemed." @@ -8818,9 +8805,8 @@ msgid "Your export did not contain any data." msgstr "Din eksport indeholdt ingen data." #: pretix/base/services/export.py -#, fuzzy msgid "Scheduled export failed" -msgstr "Start eksport" +msgstr "Den planlagte eksport mislykkedes" #: pretix/base/services/export.py msgid "Permission denied." @@ -8947,60 +8933,44 @@ msgid "You are receiving this email because you placed an order for {event}." msgstr "Du modtager denne e-mail, fordi du har afgivet en ordre til {event}." #: pretix/base/services/media.py -#, fuzzy -#| msgid "Invalid input type." msgid "Invalid medium type." -msgstr "Ugyldig indtastning." +msgstr "Ugyldig medietype." #: pretix/base/services/media.py -#, fuzzy -#| msgid "The selected media type is not enabled in your organizer settings." msgid "Medium type is not enabled for organizer." -msgstr "Den valgte medietype er ikke aktiveret i dine arrangør-indstillinger." +msgstr "Medie-typen er ikke aktiveret for arrangøren." #: pretix/base/services/media.py msgid "Incorrect medium type for product." -msgstr "" +msgstr "Forkert medietype for produktet." #: pretix/base/services/media.py -#, fuzzy -#| msgid "This ticket has already been redeemed." msgid "Ticket is already exchanged for reusable medium." -msgstr "Denne billet er allerede blevet indløst." +msgstr "Billetten er allerede ombyttet til et genanvendeligt medium." #: pretix/base/services/media.py -#, fuzzy -#| msgid "Reusable Medium ID" msgid "Reusable medium not found." -msgstr "ID for genanvendeligt medie" +msgstr "Genanvendeligt medie blev ikke fundet." #: pretix/base/services/media.py -#, fuzzy -#| msgid "The reusable medium has been created." msgid "Reusable medium is inactive or expired." -msgstr "Det genanvendelige medie er blevet oprettet." +msgstr "Det genanvendelige medium er inaktivt eller udløbet." #: pretix/base/services/media.py -#, fuzzy -#| msgid "The reusable medium has been created." msgid "Reusable medium not found and could not be created." -msgstr "Det genanvendelige medie er blevet oprettet." +msgstr "Genanvendeligt medie blev ikke fundet og kunne ikke oprettes." #: pretix/base/services/media.py -#, fuzzy -#| msgid "Reusable media type" msgid "Reusable medium already exists." -msgstr "Genanvendelig medietype" +msgstr "Det genanvendelige medie findes allerede." #: pretix/base/services/media.py -#, fuzzy -#| msgid "The reusable medium has been created." msgid "Reusable medium could not be created." -msgstr "Det genanvendelige medie er blevet oprettet." +msgstr "Det var ikke muligt at oprette det genanvendelige medie." #: pretix/base/services/media.py msgid "Product does not support medium exchange." -msgstr "" +msgstr "Produktet understøtter ikke udskiftning af medier." #: pretix/base/services/memberships.py #, python-brace-format @@ -9164,6 +9134,7 @@ msgstr "En voucher kan ikke oprettes uden kode." msgid "" "Voucher codes must be unique. Code \"{code}\" already exists in this import." msgstr "" +"Kuponkoder skal være unikke. Koden \"{code}\" findes allerede i denne import." #: pretix/base/services/modelimport.py #, python-brace-format @@ -9172,13 +9143,19 @@ msgid "" msgid_plural "" "Voucher codes must be unique. Import contains existing voucher codes {code}." msgstr[0] "" +"Rabatkoder skal være unikke. Importen indeholder den eksisterende rabatkode " +"{code}." msgstr[1] "" +"Rabatkoder skal være unikke. Importen indeholder de eksisterende rabatkoder " +"{code}." #: pretix/base/services/modelimport.py msgid "" "Vouchers could not be imported, probably due to a voucher code already being " "in use." msgstr "" +"Rabatkoder kunne ikke importeres, sandsynligvis fordi en kuponkode allerede " +"var i brug." #: pretix/base/services/orders.py msgid "" @@ -9462,16 +9439,12 @@ msgstr "" "gavekort." #: pretix/base/services/orders.py -#, fuzzy -#| msgid "" -#| "You cannot change the price of a position that has been used to issue a " -#| "gift card." msgid "" "You cannot change the ticket secret of a position that has been used to " "issue a gift card." msgstr "" -"Du kan ikke ændre prisen af en post, som er blevet brugt til at udstede et " -"gavekort." +"Du kan ikke ændre billetkoden for en position, der er blevet brugt til at " +"udstede et gavekort." #: pretix/base/services/orders.py #, python-brace-format @@ -9558,10 +9531,9 @@ msgid "Something happened in your event after the export, please try again." msgstr "Der skete noget i din begivenhed efter eksporten, prøv venligst igen." #: pretix/base/services/shredder.py -#, fuzzy, python-format -#| msgid "Data shredding completed" +#, python-format msgid "Data shredding completed for %(event)s" -msgstr "Makulering af data afsluttet" +msgstr "Datadestruktion for %(event)s er afsluttet" #: pretix/base/services/stats.py msgid "Uncategorized" @@ -9738,29 +9710,24 @@ msgstr "" "ind under købet." #: pretix/base/settings.py -#, fuzzy -#| msgid "Activate re-usable media" msgid "Activate reusable media" msgstr "Aktiver genanvendelige medier" #: pretix/base/settings.py -#, fuzzy -#| msgid "" -#| "The re-usable media feature allows you to connect tickets and gift cards " -#| "with physical media such as wristbands or chip cards that may be re-used " -#| "for different tickets or gift cards later." msgid "" "The reusable media feature allows you to connect tickets and gift cards with " "physical media such as wristbands or chip cards that may be reused for " "different tickets or gift cards later." msgstr "" -"Funktionen \"Genanvendelige medier\" gør det muligt at forbinde billetter og " -"voucher med fysiske medier som armbånd eller chipkort, der kan genbruges til " -"andre billetter eller voucher senere." +"Med funktionen \"Genanvendelige medier\" kan du knytte billetter og gavekort " +"til fysiske medier såsom armbånd eller chipkort, som senere kan genbruges " +"til andre billetter eller gavekort." #: pretix/base/settings.py msgid "Enforce the usage of issued reusable media for check-in" msgstr "" +"Gennemtving, at der udelukkende anvendes de udleverede genanvendelige medier " +"ved check-in" #: pretix/base/settings.py msgid "" @@ -9768,6 +9735,10 @@ msgid "" "medium has been created and linked to a ticket. Keeping this option turned " "off will treat the reusable medium and ticket as equals." msgstr "" +"Hvis denne indstilling er aktiveret, accepteres en billetstregkode ikke " +"længere, hvis der er oprettet et genanvendeligt medie, som er knyttet til en " +"billet. Hvis denne indstilling forbliver deaktiveret, behandles det " +"genanvendelige medie og billetten på samme måde." #: pretix/base/settings.py msgid "Length of barcodes" @@ -11336,10 +11307,8 @@ msgid "We'll show this publicly to allow attendees to contact you." msgstr "Vi vil vise dette offentligt, så deltagerne kan kontakte dig." #: pretix/base/settings.py -#, fuzzy -#| msgid "Contact" msgid "Contact URL" -msgstr "Kontakt" +msgstr "Kontakt-URL" #: pretix/base/settings.py msgid "" @@ -11347,6 +11316,9 @@ msgid "" "the email address above. Please note that you still need to add a contact " "email address that will be shared with all emails you send." msgstr "" +"Hvis du indstiller dette, vil kontaktlinket i sidefoden pege hertil i stedet " +"for at bruge ovenstående e-mailadresse. Bemærk, at du stadig skal tilføje en " +"kontakt-e-mailadresse, som vil blive angivet i alle de e-mails, du sender." #: pretix/base/settings.py pretix/control/forms/event.py msgid "Imprint URL" @@ -13213,30 +13185,19 @@ msgstr "" "os." #: pretix/base/templates/pretixbase/email/cancel_confirm.txt -#, fuzzy -#| msgid "" -#| "You have requested us to cancel an event which includes a larger bulk-" -#| "refund:" msgid "You requested to cancel an event that involves a large bulk refund:" msgstr "" -"Du har bedt os om at aflyse et arrangement, der indebærer en større samlet " +"Du har anmodet om at annullere en begivenhed, der medfører en stor samlet " "refusion:" #: pretix/base/templates/pretixbase/email/cancel_confirm.txt -#, fuzzy -#| msgid "Estimated refund amount" msgid "Estimated refund" -msgstr "Anslået refusionsbeløb" +msgstr "Anslået tilbagebetaling" #: pretix/base/templates/pretixbase/email/cancel_confirm.txt -#, fuzzy -#| msgid "" -#| "Please confirm that you want to proceed by coping the following " -#| "confirmation code into the cancellation form:" msgid "To confirm, paste the following code into the cancellation form:" msgstr "" -"Bekræft venligst, at du ønsker at fortsætte ved at indsætte følgende " -"bekræftelseskode i afmeldingsformularen:" +"For at bekræfte skal du indsætte følgende kode i afbestillingsformularen:" #: pretix/base/templates/pretixbase/email/cancel_confirm.txt #, python-format @@ -13244,6 +13205,8 @@ msgid "" "Don't share this code with anyone. The %(instance)s team will never ask you " "for it." msgstr "" +"Del ikke denne kode med nogen. %(instance)s-teamet vil aldrig bede dig om " +"den." #: pretix/base/templates/pretixbase/email/cancel_confirm.txt #: pretix/base/templates/pretixbase/email/export_failed.txt @@ -13252,6 +13215,8 @@ msgid "" "Thanks, \n" "The %(instance)s Team" msgstr "" +"Tak, \n" +"%(instance)s-teamet" #: pretix/base/templates/pretixbase/email/email_footer.html #, python-format @@ -13259,51 +13224,38 @@ msgid "powered by pretix" msgstr "drevet af pretix" #: pretix/base/templates/pretixbase/email/export_failed.txt -#, fuzzy -#| msgid "Your export failed." msgid "Your scheduled export failed." -msgstr "Din eksport mislykkedes." +msgstr "Din planlagte eksport mislykkedes." #: pretix/base/templates/pretixbase/email/export_failed.txt #: pretix/control/templates/pretixcontrol/event/tax_edit.html -#, fuzzy msgid "Reason" -msgstr "Tilbagebetal bestilling" +msgstr "Begrundelse" #: pretix/base/templates/pretixbase/email/export_failed.txt -#, fuzzy -#| msgid "If your export fails five times in a row, it will no longer be sent." msgid "If an export fails five times in a row, we'll stop sending it." msgstr "" -"Hvis din eksport mislykkes fem gange i træk, vil den ikke længere blive " -"sendt." +"Hvis en eksport mislykkes fem gange i træk, vil vi stoppe med at sende den." #: pretix/base/templates/pretixbase/email/export_failed.txt -#, fuzzy -#| msgid "You can request to cancel this order." msgid "You can adjust or remove this export here:" -msgstr "Du kan anmode om at annullere denne bestilling." +msgstr "Du kan ændre eller fjerne denne eksport her:" #: pretix/base/templates/pretixbase/email/notification.html #: pretix/base/templates/pretixbase/email/notification.txt -#, fuzzy -#| msgid "You receive these emails based on your notification settings." msgid "You're receiving this email based on your notification settings." msgstr "" -"Du modtager disse e-mails i henhold til dine indstillinger for " -"notifikationer." +"Du modtager denne e-mail i henhold til dine indstillinger for meddelelser." #: pretix/base/templates/pretixbase/email/notification.html #: pretix/base/templates/pretixbase/email/notification.txt -#, fuzzy msgid "Manage settings" -msgstr "Basisindstillinger" +msgstr "Administrer indstillinger" #: pretix/base/templates/pretixbase/email/notification.html #: pretix/base/templates/pretixbase/email/notification.txt -#, fuzzy msgid "Disable all notifications" -msgstr "E-mailnotifikationer" +msgstr "Deaktiver alle notifikationer" #: pretix/base/templates/pretixbase/email/order_details.html msgid "" @@ -13362,25 +13314,7 @@ msgid "Contact" msgstr "Kontakt" #: pretix/base/templates/pretixbase/email/shred_completed.txt -#, fuzzy, python-format -#| msgid "" -#| "Hello,\n" -#| "\n" -#| "we hereby confirm that the following data shredding job has been " -#| "completed:\n" -#| "\n" -#| "Organizer: %(organizer)s\n" -#| "\n" -#| "Event: %(event)s\n" -#| "\n" -#| "Data selection: %(shredders)s\n" -#| "\n" -#| "Start time: %(start_time)s (new data added after this time might not have " -#| "been deleted)\n" -#| "\n" -#| "Best regards,\n" -#| "\n" -#| "Your %(instance)s team\n" +#, python-format msgid "" "Hello,\n" "\n" @@ -13398,20 +13332,19 @@ msgid "" msgstr "" "Hej,\n" "\n" -"vi bekræfter hermed, at følgende datadestruktionsopgave er afsluttet:\n" +"Følgende datadestruktionsopgave er afsluttet:\n" "\n" -"Arrangør: %(organizer)s\n" +"- Arrangør: %(organizer)s\n" +"- Begivenhed: %(event)s\n" +"- Dataudvælgelse: %(shredders)s\n" +"- Starttidspunkt: %(start_time)s\n" "\n" -"Begivenhed: %(event)s\n" +"Data, der er tilføjet til begivenheden efter starttidspunktet, er muligvis " +"ikke blevet slettet.\n" "\n" -"Dataudvælgelse: %(shredders)s\n" +"Med venlig hilsen, \n" "\n" -"Starttidspunkt: %(start_time)s (nye data tilføjet efter dette tidspunkt er " -"muligvis ikke blevet slettet)\n" -"\n" -"Med venlig hilsen,\n" -"\n" -"Dit %(instance)s-team\n" +"%(instance)s-teamet\n" #: pretix/base/templates/pretixbase/forms/widgets/checkbox_sales_channel_option.html msgid "" @@ -13443,22 +13376,23 @@ msgstr "%(number)s dage %(relation)s %(relation_to)s på %(time_of_day)s" #: pretix/base/templates/pretixbase/framebreak.html #: pretix/presale/templates/pretixpresale/event/cookies.html msgid "Please continue in a new tab" -msgstr "" +msgstr "Fortsæt venligst i en ny fane" #: pretix/base/templates/pretixbase/framebreak.html msgid "For security reasons, the following step is only possible in a new tab." msgstr "" +"Af sikkerhedsmæssige årsager kan det følgende trin kun udføres i en ny fane." #: pretix/base/templates/pretixbase/framebreak.html msgid "" "If the new tab did not open automatically, please click the following button:" msgstr "" +"Hvis den nye fane ikke åbnede automatisk, skal du klikke på følgende knap:" #: pretix/base/templates/pretixbase/framebreak.html #: pretix/presale/templates/pretixpresale/event/cookies.html -#, fuzzy msgid "Continue in new tab" -msgstr "Opret gruppe" +msgstr "Fortsæt i en ny fane" #: pretix/base/templates/pretixbase/redirect.html msgid "Redirect" @@ -15184,13 +15118,11 @@ msgstr "Søg efter e-mailadresse eller emne" #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/orders/refunds.html msgid "Source" -msgstr "" +msgstr "Kilde" #: pretix/control/forms/filter.py -#, fuzzy -#| msgid "All vouchers" msgid "All sources" -msgstr "Alle vouchere" +msgstr "Alle kilder" #: pretix/control/forms/filter.py msgid "Team actions" @@ -15201,21 +15133,16 @@ msgid "Customer actions" msgstr "Kundehandlinger" #: pretix/control/forms/filter.py -#, fuzzy -#| msgid "Device status" msgid "Device actions" -msgstr "Enhedsstatus" +msgstr "Enhedshandlinger" #: pretix/control/forms/filter.py -#, fuzzy -#| msgid "Order email" msgid "User email" -msgstr "Bestillings-e-mail" +msgstr "Brugerens e-mailadresse" #: pretix/control/forms/filter.py pretix/control/navigation.py -#, fuzzy msgid "All users" -msgstr "Alle vouchere" +msgstr "Alle brugere" #: pretix/control/forms/global_settings.py msgid "Additional footer text" @@ -16724,40 +16651,34 @@ msgid "" "because at least one of the selected vouchers has already been redeemed " "%(max_redeemed)s times." msgstr "" +"Du kan ikke reducere det maksimale antal indløsninger til %(max_usages)s, da " +"mindst én af de valgte kuponer allerede er blevet indløst %(max_redeemed)s " +"gange." #: pretix/control/forms/vouchers.py -#, fuzzy -#| msgid "" -#| "You cannot create a voucher that blocks quota as the selected product or " -#| "quota is currently sold out or completely reserved." msgid "" "You cannot create a voucher that allows selection of a quota but has no date " "selected." msgstr "" -"Du kan ikke oprette en rabatkode der blokerer en kvote idet det valgte " -"produkt eller kvote pt. er udsolgt eller fuldt reserveret." +"Du kan ikke oprette en kupon, hvor det er muligt at vælge en kvote, men hvor " +"der ikke er valgt nogen dato." #: pretix/control/forms/vouchers.py -#, fuzzy -#| msgid "The selected product does not allow to select a seat." msgid "The selected quota does not match the selected subevent." -msgstr "Det valgte produkt tillader ikke, at du vælger en plads." +msgstr "Den valgte kvote stemmer ikke overens med den valgte underbegivenhed." #: pretix/control/forms/vouchers.py -#, fuzzy -#| msgid "" -#| "There is not enough quota available on quota \"{}\" to perform the " -#| "operation." msgid "There is no sufficient quota available to perform this change." msgstr "" -"Der er ikke enheder nok tilbage på kvoten \"{}\" til at udføre denne " -"operation." +"Der er ikke tilstrækkelig kvote til rådighed til at gennemføre denne ændring." #: pretix/control/forms/vouchers.py msgid "" "Changing the maximum number of usages in bulk is not supported if any of the " "selected vouchers is assigned a seat." msgstr "" +"Det er ikke muligt at ændre det maksimale antal anvendelser samlet, hvis en " +"af de valgte kuponer er tildelt en plads." #: pretix/control/forms/vouchers.py msgctxt "subevent" @@ -16765,18 +16686,24 @@ msgid "" "Changing the date in bulk is not supported if any of the selected vouchers " "is assigned a seat." msgstr "" +"Det er ikke muligt at ændre datoen for flere billetter på én gang, hvis en " +"af de valgte billetter er tildelt en plads." #: pretix/control/forms/vouchers.py msgid "" "Changing the product to a quota is not supported if any of the selected " "vouchers is assigned a seat." msgstr "" +"Det er ikke muligt at ændre produktet til en kvote, hvis en af de valgte " +"kuponer er tildelt en plads." #: pretix/control/forms/vouchers.py msgid "" "This change cannot be completed because not all assigned seats of the " "vouchers are still available" msgstr "" +"Denne ændring kan ikke gennemføres, da ikke alle de tildelte pladser på " +"billetterne stadig er ledige" #: pretix/control/forms/vouchers.py msgid "Codes" @@ -17897,16 +17824,12 @@ msgid "The reusable medium has been changed." msgstr "Det genanvendelige medie er blevet udskiftet." #: pretix/control/logdisplay.py -#, fuzzy -#| msgid "The new member has been added to the team." msgid "A new ticket has been added to the medium." -msgstr "Det nye medlem er blevet føjet til gruppen." +msgstr "Der er blevet tilføjet en ny billet til mediet." #: pretix/control/logdisplay.py -#, fuzzy -#| msgid "{user} has been removed from the team." msgid "A ticket has been removed from the medium." -msgstr "{user} er fjernet fra gruppen." +msgstr "En billet er blevet fjernet fra mediet." #: pretix/control/logdisplay.py msgid "The medium has been connected to a new ticket." @@ -17918,6 +17841,8 @@ msgid "" "The ticket #{positionid} was exchanged for reusable medium " "{medium_identifier}." msgstr "" +"Billetten #{positionid} blev ombyttet til et genanvendeligt medie " +"{medium_identifier}." #: pretix/control/logdisplay.py msgid "The medium has been connected to a new gift card." @@ -18328,14 +18253,14 @@ msgid "Payment {local_id} has been confirmed." msgstr "Betalingen {local_id} er blevet bekræftet." #: pretix/control/logdisplay.py -#, fuzzy, python-brace-format +#, python-brace-format msgid "Payment {local_id} has been canceled." -msgstr "Bestillingen er blevet annulleret." +msgstr "Betalingen {local_id} er blevet annulleret." #: pretix/control/logdisplay.py -#, fuzzy, python-brace-format +#, python-brace-format msgid "Canceling payment {local_id} has failed." -msgstr "Bestillingen er blevet annulleret." +msgstr "Det lykkedes ikke at annullere betalingen {local_id}." #: pretix/control/logdisplay.py #, fuzzy, python-brace-format @@ -21795,54 +21720,51 @@ msgstr "" #: pretix/control/templates/pretixcontrol/global_sysreport.html msgid "January" -msgstr "" +msgstr "januar" #: pretix/control/templates/pretixcontrol/global_sysreport.html msgid "February" -msgstr "" +msgstr "februar" #: pretix/control/templates/pretixcontrol/global_sysreport.html -#, fuzzy msgid "March" -msgstr "Marts" +msgstr "marts" #: pretix/control/templates/pretixcontrol/global_sysreport.html msgid "April" -msgstr "" +msgstr "april" #: pretix/control/templates/pretixcontrol/global_sysreport.html -#, fuzzy -#| msgid "Day" msgid "May" -msgstr "Dag" +msgstr "maj" #: pretix/control/templates/pretixcontrol/global_sysreport.html msgid "June" -msgstr "" +msgstr "juni" #: pretix/control/templates/pretixcontrol/global_sysreport.html msgid "July" -msgstr "" +msgstr "juli" #: pretix/control/templates/pretixcontrol/global_sysreport.html msgid "August" -msgstr "" +msgstr "august" #: pretix/control/templates/pretixcontrol/global_sysreport.html msgid "September" -msgstr "September" +msgstr "september" #: pretix/control/templates/pretixcontrol/global_sysreport.html msgid "October" -msgstr "" +msgstr "oktober" #: pretix/control/templates/pretixcontrol/global_sysreport.html msgid "November" -msgstr "November" +msgstr "november" #: pretix/control/templates/pretixcontrol/global_sysreport.html msgid "December" -msgstr "December" +msgstr "december" #: pretix/control/templates/pretixcontrol/global_sysreport.html #, fuzzy -- 2.54.0 From 1a6edae769dbdae0d2243cf2a9c825362a46eaef Mon Sep 17 00:00:00 2001 From: Nikolai Date: Fri, 31 Jul 2026 11:10:31 +0200 Subject: [PATCH 016/249] Translations: Update Danish Currently translated at 58.0% (151 of 260 strings) Translation: pretix/pretix (JavaScript parts) Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/da/ powered by weblate --- src/pretix/locale/da/LC_MESSAGES/djangojs.po | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/pretix/locale/da/LC_MESSAGES/djangojs.po b/src/pretix/locale/da/LC_MESSAGES/djangojs.po index 393cfe1e4..99705d68c 100644 --- a/src/pretix/locale/da/LC_MESSAGES/djangojs.po +++ b/src/pretix/locale/da/LC_MESSAGES/djangojs.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-06 15:52+0000\n" -"PO-Revision-Date: 2026-04-22 18:00+0000\n" +"PO-Revision-Date: 2026-07-31 18:00+0000\n" "Last-Translator: Nikolai \n" "Language-Team: Danish \n" @@ -16,7 +16,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 5.17\n" +"X-Generator: Weblate 2026.7.1\n" #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js msgid "Marked as paid" @@ -1306,62 +1306,62 @@ msgstr "" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "January" -msgstr "Januar" +msgstr "januar" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "February" -msgstr "Februar" +msgstr "februar" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "March" -msgstr "Marts" +msgstr "marts" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "April" -msgstr "April" +msgstr "april" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "May" -msgstr "Maj" +msgstr "maj" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "June" -msgstr "Juni" +msgstr "juni" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "July" -msgstr "Juli" +msgstr "juli" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "August" -msgstr "August" +msgstr "august" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "September" -msgstr "September" +msgstr "september" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "October" -msgstr "Oktober" +msgstr "oktober" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "November" -msgstr "November" +msgstr "november" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "December" -msgstr "December" +msgstr "december" #~ msgid "iDEAL" #~ msgstr "iDEAL" -- 2.54.0 From 1ea1c1789cd6de973f80edca916198779b47a1ce Mon Sep 17 00:00:00 2001 From: szurofkamarciidfbe08444ef04788 Date: Sat, 1 Aug 2026 14:57:12 +0200 Subject: [PATCH 017/249] Translations: Update Hungarian Currently translated at 99.6% (6362 of 6387 strings) Translation: pretix/pretix Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/hu/ powered by weblate --- src/pretix/locale/hu/LC_MESSAGES/django.po | 24 +++++----------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/pretix/locale/hu/LC_MESSAGES/django.po b/src/pretix/locale/hu/LC_MESSAGES/django.po index 93f7e52b3..18d285489 100644 --- a/src/pretix/locale/hu/LC_MESSAGES/django.po +++ b/src/pretix/locale/hu/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-30 13:52+0000\n" -"PO-Revision-Date: 2026-07-23 15:35+0000\n" +"PO-Revision-Date: 2026-08-01 21:00+0000\n" "Last-Translator: szurofkamarciidfbe08444ef04788 \n" "Language-Team: Hungarian \n" @@ -1377,10 +1377,8 @@ msgid "Membership type" msgstr "Tagságtípus" #: pretix/base/exporters/customers.py -#, fuzzy -#| msgid "Purchase time" msgid "Purchase ticket" -msgstr "Vásárlás időpontja" +msgstr "Jegy vásárlása" #: pretix/base/exporters/customers.py pretix/base/exporters/orderlist.py #: pretix/base/exporters/waitinglist.py pretix/base/forms/questions.py @@ -1397,10 +1395,8 @@ msgid "Start date" msgstr "Kezdő dátum" #: pretix/base/exporters/customers.py -#, fuzzy -#| msgid "Start time from" msgid "Start time" -msgstr "Kezdési időpont ettől" +msgstr "Kezdési időpont" #: pretix/base/exporters/customers.py pretix/base/exporters/orderlist.py #: pretix/base/exporters/waitinglist.py pretix/base/models/memberships.py @@ -1413,10 +1409,8 @@ msgid "End date" msgstr "Záró dátum" #: pretix/base/exporters/customers.py -#, fuzzy -#| msgid "End: %(time)s" msgid "End time" -msgstr "Vége: %(time)s" +msgstr "Záró időpont" #: pretix/base/exporters/dekodi.py pretix/base/exporters/invoices.py msgctxt "export_category" @@ -4641,10 +4635,6 @@ msgid "This event is remote or partially remote." msgstr "Ez az rendezvény távoli vagy részben távoli." #: pretix/base/models/event.py -#, fuzzy -#| msgid "" -#| "This will be used to let users know if the event is in a different " -#| "timezone and let’s us calculate users’ local times." msgid "" "This will be used to let users know if the event is in a different timezone, " "and to let us calculate the local time of a user." @@ -7578,15 +7568,11 @@ msgid "This gift card was used in the meantime. Please try again." msgstr "Ezt az ajándékkártyát időközben felhasználták. Kérjük, próbálja újra." #: pretix/base/payment.py -#, fuzzy -#| msgid "" -#| "This payment provider does not exist or the respective plugin is disabled." msgid "" "This payment provider exists for historical purposes only and is no longer " "usable." msgstr "" -"Ez a fizetési szolgáltató nem létezik, vagy a hozzá tartozó bővítmény le van " -"tiltva." +"Ez a fizetési szolgáltató egy korábbi funkció volt, már nem használható." #: pretix/base/pdf.py msgid "Ticket code (barcode content)" -- 2.54.0 From 5d8ba189faa2d88eac6945b7520fb153931feb84 Mon Sep 17 00:00:00 2001 From: "Luca Sorace \\\"Stranck" Date: Sun, 2 Aug 2026 15:58:26 +0200 Subject: [PATCH 018/249] Translations: Update Italian Currently translated at 39.6% (2530 of 6387 strings) Translation: pretix/pretix Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/ powered by weblate --- src/pretix/locale/it/LC_MESSAGES/django.po | 105 ++++++++++++++------- 1 file changed, 72 insertions(+), 33 deletions(-) diff --git a/src/pretix/locale/it/LC_MESSAGES/django.po b/src/pretix/locale/it/LC_MESSAGES/django.po index cd54a05d9..2d1d82a7b 100644 --- a/src/pretix/locale/it/LC_MESSAGES/django.po +++ b/src/pretix/locale/it/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-30 13:52+0000\n" -"PO-Revision-Date: 2026-05-12 04:00+0000\n" -"Last-Translator: Stefano Campus \n" +"PO-Revision-Date: 2026-08-02 22:00+0000\n" +"Last-Translator: \"Luca Sorace \\\"Stranck\\\"\" \n" "Language-Team: Italian \n" "Language: it\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 5.17.1\n" +"X-Generator: Weblate 2026.7.1\n" #: pretix/_base_settings.py msgid "English" @@ -149,7 +149,7 @@ msgstr "Spagnolo (America Latina)" #: pretix/_base_settings.py msgid "Thai" -msgstr "" +msgstr "Tailandese" #: pretix/_base_settings.py msgid "Turkish" @@ -413,7 +413,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 "Sei stato invitato ad entrare in %(organizer)s" #: pretix/api/serializers/organizer.py pretix/control/views/organizer.py msgid "This user already has been invited for this team." @@ -605,6 +605,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 "" +"Questo include gli eventi relativi quali la creazione, cancellazione, " +"apertura o chiusura delle quote. Nessun webhook verrà inviato per modifiche " +"alla disponibilità." #: pretix/api/webhooks.py msgid "Shop taken live" @@ -1234,8 +1237,9 @@ msgstr "Indirizzi Email (file di testo)" #: pretix/control/templates/pretixcontrol/organizers/customer.html #: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html #: pretix/presale/views/customer.py +#, fuzzy msgid "Memberships" -msgstr "" +msgstr "Membership" #: pretix/base/exporters/customers.py pretix/base/models/customers.py #: pretix/control/templates/pretixcontrol/organizers/customer.html @@ -3030,11 +3034,12 @@ msgid "" "The field \"%(label)s\" may not contain special characters such as " "\"%(chars)s\"." msgstr "" +"Il campo \"%(label)s\" non può contenere i caratteri speciali \"%(chars)s\"." #: pretix/base/forms/questions.py #, python-format msgid "The field \"%(label)s\" may not contain an URL (%(url)s)." -msgstr "" +msgstr "Il campo \"%(label)s\" non può contenere un URL (%(url)s)." #: pretix/base/forms/questions.py msgctxt "phonenumber" @@ -4272,8 +4277,9 @@ msgid "Ticket type not allowed here" msgstr "Biglietto non consentito qui" #: pretix/base/models/checkin.py +#, fuzzy msgid "Ticket code is ambiguous on list" -msgstr "Codice biglietto ambiguo nella lista" +msgstr "Il codice del biglietto è ambiguo sulla lista" #: pretix/base/models/checkin.py msgid "Server error" @@ -6167,14 +6173,15 @@ msgid "bounced" msgstr "rimbalzato" #: pretix/base/models/media.py +#, fuzzy msgctxt "reusable_medium" msgid "Claim token" -msgstr "" +msgstr "Applica token" #: pretix/base/models/media.py msgctxt "reusable_medium" msgid "Label" -msgstr "" +msgstr "Etichetta" #: pretix/base/models/media.py #, fuzzy @@ -6188,6 +6195,9 @@ msgid "" "validity. If multiple tickets are valid at once, this will lead to failed " "check-ins." msgstr "" +"Se colleghi più di un biglietto, assicurati che la loro validità non si " +"sovrapponga. Se più biglietti sono validi contemporaneamente i check-in " +"andranno in errore." #: pretix/base/models/memberships.py #: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html @@ -7676,6 +7686,8 @@ msgid "" "This payment provider exists for historical purposes only and is no longer " "usable." msgstr "" +"Questo provider di pagamenti esiste per ragioni storiche e non ne è più " +"possibile l'utilizzo." #: pretix/base/pdf.py msgid "Ticket code (barcode content)" @@ -7843,7 +7855,7 @@ msgstr "Giorno di inizio evento" #: pretix/base/pdf.py pretix/base/services/checkin.py #: pretix/control/forms/filter.py msgid "Friday" -msgstr "venerdì" +msgstr "Venerdì" #: pretix/base/pdf.py msgid "Event end date and time" @@ -8387,7 +8399,7 @@ msgstr "Evento annullato" #: pretix/base/services/cancelevent.py msgid "Confirm event cancellation and bulk refund" -msgstr "" +msgstr "Conferma la cancellazione dell'evento e i rimborsi di tutti gli ordini" #: pretix/base/services/cart.py pretix/base/services/modelimport.py #: pretix/base/services/orders.py @@ -9333,6 +9345,8 @@ msgstr "Non è possibile creare un buono senza un codice." msgid "" "Voucher codes must be unique. Code \"{code}\" already exists in this import." msgstr "" +"Il codice del voucher deve essere univoco. \"{code}\" esiste già in questo " +"import." #: pretix/base/services/modelimport.py #, python-brace-format @@ -9341,13 +9355,19 @@ msgid "" msgid_plural "" "Voucher codes must be unique. Import contains existing voucher codes {code}." msgstr[0] "" +"Il codice del voucher deve essere univoco. \"{code}\" esiste già in questo " +"import." msgstr[1] "" +"Il codice dei voucher deve essere univoco. \"{code}\" esiste già in questo " +"import." #: pretix/base/services/modelimport.py msgid "" "Vouchers could not be imported, probably due to a voucher code already being " "in use." msgstr "" +"Non è stato possibile importare i voucher, probabilmente perché il codice di " +"un voucher è già in uso." #: pretix/base/services/orders.py msgid "" @@ -10267,6 +10287,10 @@ msgid "" "ID in all countries. VAT ID will be required for all business addresses in " "the selected countries." msgstr "" +"La partita IVA è opzionale di default, perché non tutte le aziende la " +"posseggono in tutte le nazioni. Il codice della partita IVA sarà " +"obbligatorio per tutti gli indirizzi aziendali locati nelle nazioni " +"selezionate." #: pretix/base/settings.py msgid "Invoice address explanation" @@ -11316,50 +11340,60 @@ msgstr "" #: pretix/base/settings.py msgid "Customers can change the variation of the products they purchased" -msgstr "" +msgstr "I clienti potranno cambiare la variation dei prodotti già acquistati" #: pretix/base/settings.py -#, fuzzy msgid "Customers can change their selected add-on products" -msgstr "I clienti non possono più modificare i loro ordini" +msgstr "I clienti non potranno più modificare i gli add-on selezionati" #: pretix/base/settings.py msgid "" "Only allow changes if the resulting price is higher or equal than the " "previous price." msgstr "" +"Permetti le modifiche solamente se il prezzo finale è maggiore o uguale al " +"prezzo precedente." #: pretix/base/settings.py msgid "" "Only allow changes if the resulting price is higher than the previous price." msgstr "" +"Permetti le modifiche solamente se il prezzo finale è maggiore del prezzo " +"precedente." #: pretix/base/settings.py msgid "" "Only allow changes if the resulting price is equal to the previous price." msgstr "" +"Permetti le modifiche solamente se il prezzo finale è uguale al prezzo " +"precedente." #: pretix/base/settings.py msgid "" "Allow changes regardless of price, as long as no refund is required (i.e. " "the resulting price is not lower than what has already been paid)." msgstr "" +"Permetti le modifiche a prescindere del prezzo, a meno che non sia " +"necessario un rimborso (Il prezzo finale non è inferiore a quanto il cliente " +"ha già pagato)." #: pretix/base/settings.py msgid "Allow changes regardless of price, even if this results in a refund." msgstr "" +"Permetti le modifiche a prescindere del prezzo, anche se portano ad un " +"rimborso." #: pretix/base/settings.py msgid "Requirement for changed prices" -msgstr "" +msgstr "Requisiti per il cambiamento di prezzi" #: pretix/base/settings.py msgid "Do not allow changes after" -msgstr "" +msgstr "Non permettere modifiche dopo il" #: pretix/base/settings.py msgid "Allow change even though the ticket has already been checked in" -msgstr "" +msgstr "Permetti modifiche anche se il biglietto ha già effettuato il check-in" #: pretix/base/settings.py msgid "" @@ -11369,10 +11403,16 @@ msgid "" "in individually. Use with care, and preferably only in combination with a " "limitation on price changes above." msgstr "" +"Di default, le modifiche degli ordini sono disabilitate dopo che è stato " +"effettuato il check-in con qualsiasi biglietto contenuto nell'ordine. " +"Abilitando questa opzione, disabiliterai questo controllo. Non sarà comunque " +"possibile rimuovere un add-on con cui è già stato effettuato un check-in. " +"Usa questa opzione con attenzione e, preferibilmente, solamente in " +"combinazione con una delle limitazioni sul prezzo elencate sopra." #: pretix/base/settings.py msgid "Allow individual attendees to change their ticket" -msgstr "" +msgstr "Permetti ai singoli partecipanti di modificare il proprio biglietto" #: pretix/base/settings.py msgid "" @@ -20421,7 +20461,7 @@ msgstr "Abilita webhook" #: pretix/control/templates/pretixcontrol/organizers/device_logs.html #: pretix/control/templates/pretixcontrol/organizers/logs.html msgid "No results" -msgstr "" +msgstr "Nessun risultato" #: pretix/control/templates/pretixcontrol/event/mail.html #: pretix/control/templates/pretixcontrol/organizers/mail.html @@ -20462,7 +20502,7 @@ msgstr "" #: pretix/control/templates/pretixcontrol/vouchers/bulk.html #: pretix/plugins/sendmail/templates/pretixplugins/sendmail/send_form.html msgid "Edit" -msgstr "" +msgstr "Modifica" #: pretix/control/templates/pretixcontrol/event/mail.html #, fuzzy @@ -25479,7 +25519,7 @@ msgstr "" #: pretix/control/templates/pretixcontrol/pdf/index.html msgid "Text box" -msgstr "" +msgstr "Riquadro testo" #: pretix/control/templates/pretixcontrol/pdf/index.html #, fuzzy @@ -25520,7 +25560,7 @@ msgstr "" #: pretix/control/templates/pretixcontrol/pdf/index.html msgid "Duplicate" -msgstr "" +msgstr "Duplicato" #: pretix/control/templates/pretixcontrol/pdf/index.html msgid "Undo" @@ -27145,11 +27185,11 @@ msgstr "" #: pretix/control/views/dashboards.py msgid "Attendees (ordered)" -msgstr "" +msgstr "Partecipanti (ordini effettuati)" #: pretix/control/views/dashboards.py msgid "Attendees (paid)" -msgstr "" +msgstr "Partecipanti (ordini pagati)" #: pretix/control/views/dashboards.py #, python-brace-format @@ -31650,11 +31690,11 @@ msgstr "" #: pretix/plugins/stripe/payment.py msgid "Bancontact" -msgstr "" +msgstr "Bancontact" #: pretix/plugins/stripe/payment.py msgid "SEPA Direct Debit" -msgstr "" +msgstr "Addebito diretto SEPA" #: pretix/plugins/stripe/payment.py msgid "" @@ -31684,7 +31724,7 @@ msgstr "" #: pretix/plugins/stripe/payment.py msgid "Przelewy24" -msgstr "" +msgstr "Przelewy24" #: pretix/plugins/stripe/payment.py #, fuzzy @@ -31700,7 +31740,7 @@ msgstr "" #: pretix/plugins/stripe/payment.py msgid "WeChat Pay" -msgstr "" +msgstr "WeChat Pay" #: pretix/plugins/stripe/payment.py msgid "Swish" @@ -31853,7 +31893,7 @@ msgstr "" #: pretix/plugins/stripe/payment.py msgid "giropay" -msgstr "" +msgstr "giropay" #: pretix/plugins/stripe/payment.py msgid "" @@ -31877,7 +31917,7 @@ msgstr "" #: pretix/plugins/stripe/payment.py msgid "iDEAL | Wero" -msgstr "" +msgstr "iDEAL | Wero" #: pretix/plugins/stripe/payment.py msgid "" @@ -33621,9 +33661,8 @@ msgstr "" #: pretix/presale/templates/pretixpresale/event/fragment_cart.html #: pretix/presale/templates/pretixpresale/fragment_modals.html -#, fuzzy msgid "Renew reservation" -msgstr "Descrizione" +msgstr "Rinnova scadenza" #: pretix/presale/templates/pretixpresale/event/fragment_cart.html #, fuzzy -- 2.54.0 From 7bc79337c3910bc487a670e12a89c5ca8c7d70b6 Mon Sep 17 00:00:00 2001 From: "Luca Sorace \\\"Stranck" Date: Sun, 2 Aug 2026 15:58:28 +0200 Subject: [PATCH 019/249] Translations: Update Italian Currently translated at 99.6% (259 of 260 strings) Translation: pretix/pretix (JavaScript parts) Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/it/ powered by weblate --- src/pretix/locale/it/LC_MESSAGES/djangojs.po | 233 ++++++++----------- 1 file changed, 98 insertions(+), 135 deletions(-) diff --git a/src/pretix/locale/it/LC_MESSAGES/djangojs.po b/src/pretix/locale/it/LC_MESSAGES/djangojs.po index e049daf06..503f99a36 100644 --- a/src/pretix/locale/it/LC_MESSAGES/djangojs.po +++ b/src/pretix/locale/it/LC_MESSAGES/djangojs.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-06 15:52+0000\n" -"PO-Revision-Date: 2026-03-25 14:14+0000\n" -"Last-Translator: Pietro Isotti \n" +"PO-Revision-Date: 2026-08-02 22:00+0000\n" +"Last-Translator: \"Luca Sorace \\\"Stranck\\\"\" \n" "Language-Team: Italian \n" "Language: it\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 5.16.2\n" +"X-Generator: Weblate 2026.7.1\n" #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js msgid "Marked as paid" @@ -42,93 +42,91 @@ msgstr "Apple Pay" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "Itaú" -msgstr "" +msgstr "Ita" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js -#, fuzzy msgid "PayPal Credit" -msgstr "PayPal" +msgstr "PayPal (Pagamento a rate)" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "Credit Card" -msgstr "" +msgstr "Carta di credito" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "PayPal Pay Later" -msgstr "" +msgstr "PayPal (Acquista ora, paga dopo)" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "iDEAL | Wero" -msgstr "" +msgstr "iDEAL | Wero" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "SEPA Direct Debit" -msgstr "" +msgstr "Addebito diretto SEPA" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "Bancontact" -msgstr "" +msgstr "Bancontact" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "giropay" -msgstr "" +msgstr "giropay" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "SOFORT" -msgstr "" +msgstr "SOFORT" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js -#, fuzzy msgid "eps" -msgstr "Si" +msgstr "eps" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "MyBank" -msgstr "" +msgstr "MyBank" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "Przelewy24" -msgstr "" +msgstr "Przelewy24" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "Verkkopankki" -msgstr "" +msgstr "Verkkopankki" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "PayU" -msgstr "" +msgstr "PayU" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "BLIK" -msgstr "" +msgstr "BLIK" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "Trustly" -msgstr "" +msgstr "Trustly" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "Zimpler" -msgstr "" +msgstr "Zimpler" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "Maxima" -msgstr "" +msgstr "Maxima" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "OXXO" -msgstr "" +msgstr "OXXO" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "Boleto" -msgstr "" +msgstr "Boleto" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "WeChat Pay" -msgstr "" +msgstr "WeChat Pay" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "Mercado Pago" -msgstr "" +msgstr "Mercado Pago" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts @@ -143,7 +141,7 @@ msgstr "Stiamo processando il tuo pagamento …" #: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js msgid "Payment method unavailable" -msgstr "" +msgstr "Metodo di pagamento non disponibile" #: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js msgid "Placed orders" @@ -155,11 +153,11 @@ msgstr "Ordini pagati" #: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js msgid "Attendees (ordered)" -msgstr "" +msgstr "Partecipanti (ordini effettuati)" #: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js msgid "Attendees (paid)" -msgstr "" +msgstr "Partecipanti (ordini pagati)" #: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js msgid "Total revenue" @@ -239,11 +237,11 @@ msgstr "Eliminato" #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts msgid "Confirmed" -msgstr "" +msgstr "Confermato" #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts msgid "Approval pending" -msgstr "" +msgstr "In attesa di approvazione" #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts msgid "Redeemed" @@ -310,12 +308,13 @@ msgid "Order canceled" msgstr "Ordine cancellato" #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts +#, fuzzy msgid "Ticket code is ambiguous on list" -msgstr "" +msgstr "Il codice del biglietto è ambiguo sulla lista" #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts msgid "Order not approved" -msgstr "" +msgstr "Ordine non approvato" #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts msgid "Checked-in Tickets" @@ -430,11 +429,11 @@ msgstr "Usa i tasti Ctrl-C per copiare!" #: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue msgid "Edit" -msgstr "" +msgstr "Modifica" #: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue msgid "Visualize" -msgstr "" +msgstr "Visualizza" #: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue msgid "" @@ -442,10 +441,13 @@ msgid "" "or variations are not contained in any of your rule parts so people with " "these tickets will not get in:" msgstr "" +"La tua regola filtra sempre per prodotto o variation, ma i seguenti prodotti " +"o variation non sono presenti in nessuna parte delle tue regole. Questo " +"impedirà alle persone con questi biglietti di poter accedere all'evento:" #: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue msgid "Please double-check if this was intentional." -msgstr "" +msgstr "Per favore, controlla se è stato intenzionale." #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "All of the conditions below (AND)" @@ -489,17 +491,17 @@ msgstr "minuti" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "Duplicate" -msgstr "" +msgstr "Duplicato" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgctxt "entry_status" msgid "present" -msgstr "" +msgstr "Presente" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgctxt "entry_status" msgid "absent" -msgstr "" +msgstr "Assente" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "is one of" @@ -515,7 +517,7 @@ msgstr "è dopo" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "=" -msgstr "" +msgstr "=" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "Product" @@ -535,63 +537,55 @@ msgstr "Data e orario corrente" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "Current day of the week (1 = Monday, 7 = Sunday)" -msgstr "" +msgstr "Giorno corrente della settimana (1 = Lunedì, 7 = Domenica)" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "Current entry status" -msgstr "" +msgstr "Stato dell'ingresso corrente" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "Number of previous entries" -msgstr "Numero di inserimenti precedenti" +msgstr "Numero di precedenti ingressi" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "Number of previous entries since midnight" -msgstr "Numero di inserimenti precedenti fino a mezzanotte" +msgstr "Numero di precedenti ingressi fino a mezzanotte" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts -#, fuzzy -#| msgid "Number of previous entries" msgid "Number of previous entries since" -msgstr "Numero di inserimenti precedenti" +msgstr "Numero di precedenti ingressi dal" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts -#, fuzzy -#| msgid "Number of previous entries" msgid "Number of previous entries before" -msgstr "Numero di inserimenti precedenti" +msgstr "Numero di precedenti ingressi prima del" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "Number of days with a previous entry" -msgstr "Nunmero di giorni con un inserimento precedente" +msgstr "Numero di giorni con un precedente ingresso" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts -#, fuzzy -#| msgid "Number of days with a previous entry" msgid "Number of days with a previous entry since" -msgstr "Nunmero di giorni con un inserimento precedente" +msgstr "Numero di giorni con un precedente ingresso dal" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts -#, fuzzy -#| msgid "Number of days with a previous entry" msgid "Number of days with a previous entry before" -msgstr "Nunmero di giorni con un inserimento precedente" +msgstr "Numero di giorni con un precedente ingresso prima del" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "Minutes since last entry (-1 on first entry)" -msgstr "" +msgstr "Minuti dall'ultimo ingresso (-1 al primo ingresso)" #: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts msgid "Minutes since first entry (-1 on first entry)" -msgstr "" +msgstr "Minuti dal primo ingresso (-1 al primo ingresso)" #: pretix/static/pretixcontrol/js/ui/checkinrules/django-interop.ts msgid "Error: Product not found!" -msgstr "" +msgstr "Errore: Prodotto non trovato!" #: pretix/static/pretixcontrol/js/ui/checkinrules/django-interop.ts msgid "Error: Variation not found!" -msgstr "" +msgstr "Errore: Variation non trovata!" #: pretix/static/pretixcontrol/js/ui/editor.js msgid "Check-in QR" @@ -606,16 +600,12 @@ msgid "Group of objects" msgstr "Gruppo di oggetti" #: pretix/static/pretixcontrol/js/ui/editor.js -#, fuzzy -#| msgid "Text object" msgid "Text object (deprecated)" -msgstr "Oggetto testo" +msgstr "Oggetto testo (deprecato)" #: pretix/static/pretixcontrol/js/ui/editor.js -#, fuzzy -#| msgid "Text object" msgid "Text box" -msgstr "Oggetto testo" +msgstr "Riquadro testo" #: pretix/static/pretixcontrol/js/ui/editor.js msgid "Barcode area" @@ -662,25 +652,24 @@ msgid "Unknown error." msgstr "Errore sconosciuto." #: pretix/static/pretixcontrol/js/ui/main.js -#, fuzzy -#| msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and will provide excellent accessibility." -msgstr "Il colore scelto ha un ottimo contrasto ed è molto leggibile!" +msgstr "Il colore scelto ha un ottimo contrasto ed è molto leggibile." #: pretix/static/pretixcontrol/js/ui/main.js -#, fuzzy -#| msgid "Your color has decent contrast and is probably good-enough to read!" msgid "" "Your color has decent contrast and is sufficient for minimum accessibility " "requirements." msgstr "" -"Il colore scelto ha un buon contrasto e probabilmente è abbastanza leggibile!" +"Il colore scelto ha un buon contrasto ed è sufficiente per i requisiti di " +"accessibilità." #: pretix/static/pretixcontrol/js/ui/main.js msgid "" "Your color has insufficient contrast to white. Accessibility of your site " "will be impacted." msgstr "" +"Il colore scelto ha un contrasto insufficiente rispetto al bianco. " +"L'accessibilità nel tuo sito sarà ridotta." #: pretix/static/pretixcontrol/js/ui/main.js msgid "Search query" @@ -700,11 +689,11 @@ msgstr "Solo i selezionati" #: pretix/static/pretixcontrol/js/ui/main.js msgid "Enter page number between 1 and %(max)s." -msgstr "" +msgstr "Inserisci il numero di pagina tra 1 e %(max)s." #: pretix/static/pretixcontrol/js/ui/main.js msgid "Invalid page number." -msgstr "" +msgstr "Numero di pagina invalido." #: pretix/static/pretixcontrol/js/ui/main.js msgid "Use a different name internally" @@ -723,10 +712,8 @@ msgid "Calculating default price…" msgstr "Calcolando il prezzo di default…" #: pretix/static/pretixcontrol/js/ui/plugins.js -#, fuzzy -#| msgid "Search results" msgid "No results" -msgstr "Risultati ricerca" +msgstr "Nessun risultato" #: pretix/static/pretixcontrol/js/ui/question.js msgid "Others" @@ -756,39 +743,33 @@ msgstr "Carrello scaduto" #: pretix/static/pretixpresale/js/ui/cart.js msgid "Your cart is about to expire." -msgstr "" +msgstr "La selezione nel tuo carrello sta per scadere." #: pretix/static/pretixpresale/js/ui/cart.js msgid "The items in your cart are reserved for you for one minute." msgid_plural "The items in your cart are reserved for you for {num} minutes." -msgstr[0] "Gli elementi nel tuo carrello sono riservati per 1 minuto." +msgstr[0] "Gli elementi nel tuo carrello sono riservati per un minuto." msgstr[1] "Gli elementi nel tuo carrello sono riservati per {num} minuti." #: pretix/static/pretixpresale/js/ui/cart.js -#, fuzzy -#| msgid "Cart expired" msgid "Your cart has expired." -msgstr "Carrello scaduto" +msgstr "Il tuo carrello è scaduto." #: pretix/static/pretixpresale/js/ui/cart.js -#, fuzzy -#| msgid "" -#| "The items in your cart are no longer reserved for you. You can still " -#| "complete your order as long as they’re available." msgid "" "The items in your cart are no longer reserved for you. You can still " "complete your order as long as they're available." msgstr "" -"Gli articoli nel tuo carrello non sono più riservati per te. Puoi ancora " +"Gli articoli nel tuo carrello non sono più riservati a te. Puoi ancora " "completare il tuo ordine finché sono disponibili." #: pretix/static/pretixpresale/js/ui/cart.js msgid "Do you want to renew the reservation period?" -msgstr "" +msgstr "Vuoi rinnovare la scadenza del tuo carrello?" #: pretix/static/pretixpresale/js/ui/cart.js msgid "Renew reservation" -msgstr "" +msgstr "Rinnova scadenza" #: pretix/static/pretixpresale/js/ui/main.js msgid "The organizer keeps %(currency)s %(amount)s" @@ -807,69 +788,64 @@ msgid "Your local time:" msgstr "Ora locale:" #: pretix/static/pretixpresale/js/walletdetection.js -#, fuzzy -#| msgid "Apple Pay" msgid "Google Pay" -msgstr "Apple Pay" +msgstr "Google Pay" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgctxt "widget" msgid "Quantity" -msgstr "" +msgstr "Quantità" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgctxt "widget" msgid "Decrease quantity" -msgstr "" +msgstr "Diminuisci quantità" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgctxt "widget" msgid "Increase quantity" -msgstr "" +msgstr "Aumenta quantità" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgctxt "widget" msgid "Filter events by" -msgstr "" +msgstr "Filtra eventi per" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgctxt "widget" msgid "Filter" -msgstr "" +msgstr "Filtra" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgctxt "widget" msgid "Price" -msgstr "" +msgstr "Prezzo" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts #, javascript-format msgctxt "widget" msgid "Original price: %s" -msgstr "" +msgstr "Prezzo originale: %s" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts #, javascript-format msgctxt "widget" msgid "New price: %s" -msgstr "" +msgstr "Nuovo prezzo: %s" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts -#, fuzzy -#| msgctxt "widget" -#| msgid "Select %s" msgctxt "widget" msgid "Select" -msgstr "Seleziona %s" +msgstr "Seleziona" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts @@ -926,7 +902,7 @@ msgstr "da %(currency)s %(price)s" #, javascript-format msgctxt "widget" msgid "Image of %s" -msgstr "" +msgstr "Immagine di %s" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts @@ -967,27 +943,21 @@ msgstr "Disponibile solo con voucher" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts -#, fuzzy -#| msgctxt "widget" -#| msgid "currently available: %s" msgctxt "widget" msgid "Not yet available" -msgstr "attualmente disponibile: %s" +msgstr "Ancora non disponibile" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgctxt "widget" msgid "Not available anymore" -msgstr "" +msgstr "Non più disponibile" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts -#, fuzzy -#| msgctxt "widget" -#| msgid "currently available: %s" msgctxt "widget" msgid "Currently not available" -msgstr "attualmente disponibile: %s" +msgstr "Attualmente non disponibile" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts @@ -1095,18 +1065,17 @@ msgstr "Chiudi" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts -#, fuzzy -#| msgctxt "widget" -#| msgid "Resume checkout" msgctxt "widget" msgid "Close checkout" -msgstr "Ricarica checkout" +msgstr "Finisci checkout" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgctxt "widget" msgid "You cannot cancel this operation. Please wait for loading to finish." msgstr "" +"Non puoi annullare questa operazione. Per favore, aspetta che il caricamento " +"finisca." #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts @@ -1116,21 +1085,15 @@ msgstr "Continua" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts -#, fuzzy -#| msgctxt "widget" -#| msgid "Select variant %s" msgctxt "widget" msgid "Show variants" -msgstr "Seleziona variante %s" +msgstr "Mostra varianti" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts -#, fuzzy -#| msgctxt "widget" -#| msgid "Select variant %s" msgctxt "widget" msgid "Hide variants" -msgstr "Seleziona variante %s" +msgstr "Nascondi varianti" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts @@ -1236,37 +1199,37 @@ msgstr "Do" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "Monday" -msgstr "" +msgstr "Lunedì" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "Tuesday" -msgstr "" +msgstr "Martedì" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "Wednesday" -msgstr "" +msgstr "Mercoledì" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "Thursday" -msgstr "" +msgstr "Giovedì" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "Friday" -msgstr "" +msgstr "Venerdì" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "Saturday" -msgstr "" +msgstr "Sabato" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts msgid "Sunday" -msgstr "" +msgstr "Domenica" #: pretix/static/pretixpresale/js/widget/widget.js #: pretix/static/pretixpresale/widget/src/i18n.ts -- 2.54.0 From b164cf44c081ed223bae28f0346a922159b68c8d Mon Sep 17 00:00:00 2001 From: "Luca Sorace \\\"Stranck" Date: Mon, 3 Aug 2026 16:50:05 +0200 Subject: [PATCH 020/249] Translations: Update Italian Currently translated at 39.8% (2544 of 6387 strings) Translation: pretix/pretix Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/ powered by weblate --- src/pretix/locale/it/LC_MESSAGES/django.po | 37 +++++++++++++++------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/pretix/locale/it/LC_MESSAGES/django.po b/src/pretix/locale/it/LC_MESSAGES/django.po index 2d1d82a7b..61c4c2f30 100644 --- a/src/pretix/locale/it/LC_MESSAGES/django.po +++ b/src/pretix/locale/it/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-30 13:52+0000\n" -"PO-Revision-Date: 2026-08-02 22:00+0000\n" +"PO-Revision-Date: 2026-08-03 23:00+0000\n" "Last-Translator: \"Luca Sorace \\\"Stranck\\\"\" \n" "Language-Team: Italian \n" @@ -11422,15 +11422,20 @@ msgid "" "total price of the order. Such changes can always only be made by the main " "customer." msgstr "" +"Di default, solo la persona che ha effettuato l'ordine di questo biglietto " +"può eseguire modifiche. Selezionando questa opzione, anche i singoli " +"partecipanti potranno fare cambiamenti. In ogni caso, i singoli partecipanti " +"non potranno effettuare operazioni che cambiano il costo dell'ordine. Queste " +"operazioni sono possibili solamente per chi originariamente ha creato " +"l'ordine." #: pretix/base/settings.py msgid "Customers can cancel their unpaid orders" -msgstr "" +msgstr "I clienti possono annullare i loro ordini non pagati" #: pretix/base/settings.py -#, fuzzy msgid "Charge a fixed cancellation fee" -msgstr "Cancellazione di" +msgstr "Addebita una quota di cancellazione fissa" #: pretix/base/settings.py msgid "" @@ -11438,50 +11443,58 @@ msgid "" "never charged. Note that it will be your responsibility to claim the " "cancellation fee from the user." msgstr "" +"Interessa solamente gli ordini con pagamenti in attesa, la quota di " +"cancellazione non viene mai addebitata sugli ordini gratuiti. Nota: è tua " +"responsabilità collezionare la quota di cancellazione dall'utente." #: pretix/base/settings.py msgid "Charge payment, shipping and service fees" -msgstr "" +msgstr "Addebita la quota di spedizione, pagamento e servizio" #: pretix/base/settings.py msgid "Charge a percentual cancellation fee" -msgstr "" +msgstr "Addebita una quota di quota di cancellazione in percentuale" #: pretix/base/settings.py msgid "Do not allow cancellations after" -msgstr "" +msgstr "Non permettere di annullare gli ordine dopo il" #: pretix/base/settings.py msgid "Customers can cancel their paid orders" -msgstr "" +msgstr "I clienti possono annullare i loro ordini già pagati" #: pretix/base/settings.py msgid "" "Paid money will be automatically paid back if the payment method allows it. " "Otherwise, a manual refund will be created for you to process manually." msgstr "" +"Il denaro già incassato sarà automaticamente restituito se il metodo di " +"pagamento lo permette. Altrimenti, un rimborso manuale verrà creato così che " +"tu possa processarlo manualmente." #: pretix/base/settings.py pretix/control/forms/orders.py msgid "Keep a fixed cancellation fee" -msgstr "" +msgstr "Trattieni una quota di cancellazione fissa" #: pretix/base/settings.py msgid "Keep payment, shipping and service fees" -msgstr "" +msgstr "Trattieni la quota di spedizione, pagamento e servizio" #: pretix/base/settings.py pretix/control/forms/orders.py msgid "Keep a percentual cancellation fee" -msgstr "" +msgstr "Trattieni una quota di cancellazione in percentuale" #: pretix/base/settings.py msgid "Allow customers to voluntarily choose a lower refund" -msgstr "" +msgstr "Permetti si clienti di scegliere volontariamente un rimborso minore" #: pretix/base/settings.py msgid "" "With this option enabled, your customers can choose to get a smaller refund " "to support you." msgstr "" +"Con questa opzione attiva, i tuoi clienti potranno scegliere di ottenere un " +"rimborso minore al dovuto, per effettuare una donazione nei vostri confronti." #: pretix/base/settings.py msgid "" -- 2.54.0 From e3fa1aed7f4154e3180b165dbfff9e07c24ed6b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2026 14:56:53 +0200 Subject: [PATCH 021/249] Bump django-localflavor from 5.0 to 5.1 (#6449) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8a97db7cd..9594ae570 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ dependencies = [ "django-hijack==3.7.*", "django-i18nfield==1.11.*", "django-libsass==0.9", - "django-localflavor==5.0", + "django-localflavor==5.1", "django-markup", "django-oauth-toolkit==2.3.*", "django-otp==1.7.*", -- 2.54.0 From 5c448279f0abf54c8f2994c2ba6cc3bd6f3b9df4 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 5 Aug 2026 16:52:28 +0200 Subject: [PATCH 022/249] API: Make all mutating endpoints transactional (#6430) All of our API endpoints that do something in the system do at least two SQL queries, one for the actual change and one for the log entry. Often many more. We want all of this to happen in a transaction so we know an API call was executed or not at all, not half-way. --- src/pretix/api/views/checkin.py | 2 ++ src/pretix/api/views/discount.py | 4 ++++ src/pretix/api/views/event.py | 11 +++++++++++ src/pretix/api/views/exporters.py | 7 +++++++ src/pretix/api/views/item.py | 28 ++++++++++++++++++++++++++++ src/pretix/api/views/organizer.py | 3 +++ src/pretix/api/views/waitinglist.py | 4 ++++ src/pretix/api/views/webhooks.py | 4 ++++ src/pretix/plugins/sendmail/api.py | 4 ++++ 9 files changed, 67 insertions(+) diff --git a/src/pretix/api/views/checkin.py b/src/pretix/api/views/checkin.py index 7bafddd3a..1eb05b494 100644 --- a/src/pretix/api/views/checkin.py +++ b/src/pretix/api/views/checkin.py @@ -139,6 +139,7 @@ class CheckinListViewSet(viewsets.ModelViewSet): ) return qs + @transaction.atomic() def perform_create(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -153,6 +154,7 @@ class CheckinListViewSet(viewsets.ModelViewSet): ctx['event'] = self.request.event return ctx + @transaction.atomic() def perform_update(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( diff --git a/src/pretix/api/views/discount.py b/src/pretix/api/views/discount.py index 53386d3d0..92bbd671e 100644 --- a/src/pretix/api/views/discount.py +++ b/src/pretix/api/views/discount.py @@ -32,6 +32,7 @@ # 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. +from django.db import transaction from django_filters.rest_framework import DjangoFilterBackend, FilterSet from django_scopes import scopes_disabled from rest_framework import viewsets @@ -64,6 +65,7 @@ class DiscountViewSet(ConditionalListView, viewsets.ModelViewSet): 'limit_sales_channels', ) + @transaction.atomic() def perform_create(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -78,6 +80,7 @@ class DiscountViewSet(ConditionalListView, viewsets.ModelViewSet): ctx['event'] = self.request.event return ctx + @transaction.atomic() def perform_update(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -87,6 +90,7 @@ class DiscountViewSet(ConditionalListView, viewsets.ModelViewSet): data=self.request.data ) + @transaction.atomic() def perform_destroy(self, instance): if not instance.allow_delete(): raise PermissionDenied('You cannot delete this discount because it already has ' diff --git a/src/pretix/api/views/event.py b/src/pretix/api/views/event.py index 630988aac..e92f99ed8 100644 --- a/src/pretix/api/views/event.py +++ b/src/pretix/api/views/event.py @@ -257,6 +257,7 @@ class EventViewSet(viewsets.ModelViewSet): data=self.request.data ) + @transaction.atomic() def perform_create(self, serializer): copy_from = None if 'clone_from' in self.request.GET: @@ -320,6 +321,7 @@ class EventViewSet(viewsets.ModelViewSet): data=self.request.data ) + @transaction.atomic() def perform_destroy(self, instance): if not instance.allow_delete(): raise PermissionDenied('The event can not be deleted as it already contains orders. Please set \'live\'' @@ -355,6 +357,7 @@ class CloneEventViewSet(viewsets.ModelViewSet): ctx['organizer'] = self.request.organizer return ctx + @transaction.atomic() def perform_create(self, serializer): # Weird edge case: Requires settings permission on the event (to read) but also on the organizer (two write) perm_holder = (self.request.auth if isinstance(self.request.auth, (Device, TeamAPIToken)) @@ -513,6 +516,7 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet): resp['X-Page-Generated'] = date return resp + @transaction.atomic() def perform_update(self, serializer): original_data = self.get_serializer(instance=serializer.instance).data super().perform_update(serializer) @@ -529,6 +533,7 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet): data=self.request.data ) + @transaction.atomic() def perform_create(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -538,6 +543,7 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet): data=self.request.data ) + @transaction.atomic() def perform_destroy(self, instance): if not instance.allow_delete(): raise PermissionDenied('The sub-event can not be deleted as it has already been used in orders. Please set' @@ -566,6 +572,7 @@ class TaxRuleViewSet(ConditionalListView, viewsets.ModelViewSet): def get_queryset(self): return self.request.event.tax_rules.all() + @transaction.atomic() def perform_update(self, serializer): super().perform_update(serializer) serializer.instance.log_action( @@ -575,6 +582,7 @@ class TaxRuleViewSet(ConditionalListView, viewsets.ModelViewSet): data=self.request.data ) + @transaction.atomic() def perform_create(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -584,6 +592,7 @@ class TaxRuleViewSet(ConditionalListView, viewsets.ModelViewSet): data=self.request.data ) + @transaction.atomic() def perform_destroy(self, instance): if not instance.allow_delete(): raise PermissionDenied('This tax rule can not be deleted as it is currently in use.') @@ -757,6 +766,7 @@ class SeatViewSet(ConditionalListView, UpdateModelMixin, viewsets.ReadOnlyModelV } return ctx + @transaction.atomic() def perform_update(self, serializer): super().perform_update(serializer) serializer.instance.event.log_action( @@ -766,6 +776,7 @@ class SeatViewSet(ConditionalListView, UpdateModelMixin, viewsets.ReadOnlyModelV data={"seats": [serializer.instance.pk]}, ) + @transaction.atomic() def bulk_change_blocked(self, blocked): s = SeatBulkBlockInputSerializer( data=self.request.data, diff --git a/src/pretix/api/views/exporters.py b/src/pretix/api/views/exporters.py index 26aca588f..71931afcc 100644 --- a/src/pretix/api/views/exporters.py +++ b/src/pretix/api/views/exporters.py @@ -23,6 +23,7 @@ from datetime import timedelta from celery.result import AsyncResult from django.conf import settings +from django.db import transaction from django.http import Http404 from django.shortcuts import get_object_or_404 from django.utils.functional import cached_property @@ -227,6 +228,7 @@ class ScheduledEventExportViewSet(ScheduledExportersViewSet): qs = self.request.event.scheduled_exports return qs.select_related("owner") + @transaction.atomic() def perform_create(self, serializer): if not self.request.user.is_authenticated: raise PermissionDenied('Creation of exports requires user-specific API access.') @@ -257,6 +259,7 @@ class ScheduledEventExportViewSet(ScheduledExportersViewSet): )) return {e.identifier: e for e in exporters} + @transaction.atomic() def perform_update(self, serializer): if not self.request.user.is_authenticated or self.request.user != serializer.instance.owner: # This is to prevent a possible privilege escalation where user A creates a scheduled export and @@ -282,6 +285,7 @@ class ScheduledEventExportViewSet(ScheduledExportersViewSet): data=self.request.data ) + @transaction.atomic() def perform_destroy(self, instance): self.request.event.log_action( 'pretix.event.export.schedule.deleted', @@ -309,6 +313,7 @@ class ScheduledOrganizerExportViewSet(ScheduledExportersViewSet): qs = self.request.organizer.scheduled_exports return qs.select_related("owner") + @transaction.atomic() def perform_create(self, serializer): if not self.request.user.is_authenticated: raise PermissionDenied('Creation of exports requires user-specific API access.') @@ -339,6 +344,7 @@ class ScheduledOrganizerExportViewSet(ScheduledExportersViewSet): )) return {e.identifier: e for e in exporters} + @transaction.atomic() def perform_update(self, serializer): if not self.request.user.is_authenticated or self.request.user != serializer.instance.owner: # This is to prevent a possible privilege escalation where user A creates a scheduled export and @@ -389,6 +395,7 @@ class ScheduledOrganizerExportViewSet(ScheduledExportersViewSet): data=self.request.data ) + @transaction.atomic() def perform_destroy(self, instance): self.request.organizer.log_action( 'pretix.organizer.export.schedule.deleted', diff --git a/src/pretix/api/views/item.py b/src/pretix/api/views/item.py index f2f094939..8250600a0 100644 --- a/src/pretix/api/views/item.py +++ b/src/pretix/api/views/item.py @@ -33,6 +33,7 @@ # License for the specific language governing permissions and limitations under the License. import django_filters +from django.db import transaction from django.db.models import Q from django.shortcuts import get_object_or_404 from django.utils.functional import cached_property @@ -109,6 +110,7 @@ class ItemViewSet(ConditionalListView, viewsets.ModelViewSet): 'limit_sales_channels', 'variations__limit_sales_channels', 'program_times' ).all() + @transaction.atomic() def perform_create(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -123,6 +125,7 @@ class ItemViewSet(ConditionalListView, viewsets.ModelViewSet): ctx['event'] = self.request.event return ctx + @transaction.atomic() def perform_update(self, serializer): original_data = self.get_serializer(instance=serializer.instance).data @@ -139,6 +142,7 @@ class ItemViewSet(ConditionalListView, viewsets.ModelViewSet): data=self.request.data ) + @transaction.atomic() def perform_destroy(self, instance): if not instance.allow_delete(): raise PermissionDenied('This item cannot be deleted because it has already been ordered ' @@ -183,6 +187,7 @@ class ItemVariationViewSet(viewsets.ModelViewSet): ctx['event'] = self.request.event return ctx + @transaction.atomic() def perform_create(self, serializer): item = self.item if not item.has_variations: @@ -197,6 +202,7 @@ class ItemVariationViewSet(viewsets.ModelViewSet): {'value': serializer.instance.value}) ) + @transaction.atomic() def perform_update(self, serializer): serializer.save(event=self.request.event) serializer.instance.item.log_action( @@ -207,6 +213,7 @@ class ItemVariationViewSet(viewsets.ModelViewSet): {'value': serializer.instance.value}) ) + @transaction.atomic() def perform_destroy(self, instance): if not instance.allow_delete(): raise PermissionDenied('This variation cannot be deleted because it has already been ordered ' @@ -249,6 +256,7 @@ class ItemBundleViewSet(viewsets.ModelViewSet): ctx['item'] = self.item return ctx + @transaction.atomic() def perform_create(self, serializer): item = get_object_or_404(Item, pk=self.kwargs['item'], event=self.request.event) serializer.save(base_item=item) @@ -259,6 +267,7 @@ class ItemBundleViewSet(viewsets.ModelViewSet): data=merge_dicts(self.request.data, {'id': serializer.instance.pk}) ) + @transaction.atomic() def perform_update(self, serializer): serializer.save(event=self.request.event) serializer.instance.base_item.log_action( @@ -268,6 +277,7 @@ class ItemBundleViewSet(viewsets.ModelViewSet): data=merge_dicts(self.request.data, {'id': serializer.instance.pk}) ) + @transaction.atomic() def perform_destroy(self, instance): super().perform_destroy(instance) instance.base_item.log_action( @@ -303,6 +313,7 @@ class ItemProgramTimeViewSet(viewsets.ModelViewSet): ctx['item'] = self.item return ctx + @transaction.atomic() def perform_create(self, serializer): item = get_object_or_404(Item, pk=self.kwargs['item'], event=self.request.event) serializer.save(item=item) @@ -313,6 +324,7 @@ class ItemProgramTimeViewSet(viewsets.ModelViewSet): data=merge_dicts(self.request.data, {'id': serializer.instance.pk}) ) + @transaction.atomic() def perform_update(self, serializer): serializer.save(event=self.request.event) serializer.instance.item.log_action( @@ -322,6 +334,7 @@ class ItemProgramTimeViewSet(viewsets.ModelViewSet): data=merge_dicts(self.request.data, {'id': serializer.instance.pk}) ) + @transaction.atomic() def perform_destroy(self, instance): super().perform_destroy(instance) instance.item.log_action( @@ -354,6 +367,7 @@ class ItemAddOnViewSet(viewsets.ModelViewSet): ctx['item'] = self.item return ctx + @transaction.atomic() def perform_create(self, serializer): item = self.item category = get_object_or_404(ItemCategory, pk=self.request.data['addon_category']) @@ -365,6 +379,7 @@ class ItemAddOnViewSet(viewsets.ModelViewSet): data=merge_dicts(self.request.data, {'ORDER': serializer.instance.position}, {'id': serializer.instance.pk}) ) + @transaction.atomic() def perform_update(self, serializer): serializer.save(event=self.request.event) serializer.instance.base_item.log_action( @@ -374,6 +389,7 @@ class ItemAddOnViewSet(viewsets.ModelViewSet): data=merge_dicts(self.request.data, {'ORDER': serializer.instance.position}, {'id': serializer.instance.pk}) ) + @transaction.atomic() def perform_destroy(self, instance): super().perform_destroy(instance) instance.base_item.log_action( @@ -403,6 +419,7 @@ class ItemCategoryViewSet(ConditionalListView, viewsets.ModelViewSet): def get_queryset(self): return self.request.event.categories.all() + @transaction.atomic() def perform_create(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -417,6 +434,7 @@ class ItemCategoryViewSet(ConditionalListView, viewsets.ModelViewSet): ctx['event'] = self.request.event return ctx + @transaction.atomic() def perform_update(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -426,6 +444,7 @@ class ItemCategoryViewSet(ConditionalListView, viewsets.ModelViewSet): data=self.request.data ) + @transaction.atomic() def perform_destroy(self, instance): for item in instance.items.all(): item.category = None @@ -458,6 +477,7 @@ class QuestionViewSet(ConditionalListView, viewsets.ModelViewSet): def get_queryset(self): return self.request.event.questions.prefetch_related('options').all() + @transaction.atomic() def perform_create(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -472,6 +492,7 @@ class QuestionViewSet(ConditionalListView, viewsets.ModelViewSet): ctx['event'] = self.request.event return ctx + @transaction.atomic() def perform_update(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -481,6 +502,7 @@ class QuestionViewSet(ConditionalListView, viewsets.ModelViewSet): data=self.request.data ) + @transaction.atomic() def perform_destroy(self, instance): instance.log_action( 'pretix.event.question.deleted', @@ -509,6 +531,7 @@ class QuestionOptionViewSet(viewsets.ModelViewSet): ctx['question'] = get_object_or_404(Question, pk=self.kwargs['question'], event=self.request.event) return ctx + @transaction.atomic() def perform_create(self, serializer): q = get_object_or_404(Question, pk=self.kwargs['question'], event=self.request.event) serializer.save(question=q) @@ -519,6 +542,7 @@ class QuestionOptionViewSet(viewsets.ModelViewSet): data=merge_dicts(self.request.data, {'ORDER': serializer.instance.position}, {'id': serializer.instance.pk}) ) + @transaction.atomic() def perform_update(self, serializer): serializer.save(event=self.request.event) serializer.instance.question.log_action( @@ -528,6 +552,7 @@ class QuestionOptionViewSet(viewsets.ModelViewSet): data=merge_dicts(self.request.data, {'ORDER': serializer.instance.position}, {'id': serializer.instance.pk}) ) + @transaction.atomic() def perform_destroy(self, instance): instance.question.log_action( 'pretix.event.question.option.deleted', @@ -586,6 +611,7 @@ class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet): serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) + @transaction.atomic() def perform_create(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -608,6 +634,7 @@ class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet): ctx['request'] = self.request return ctx + @transaction.atomic() def perform_update(self, serializer): original_data = self.get_serializer(instance=serializer.instance).data @@ -663,6 +690,7 @@ class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet): ) serializer.instance.rebuild_cache() + @transaction.atomic() def perform_destroy(self, instance): instance.log_action( 'pretix.event.quota.deleted', diff --git a/src/pretix/api/views/organizer.py b/src/pretix/api/views/organizer.py index 3551437b6..db28044b3 100644 --- a/src/pretix/api/views/organizer.py +++ b/src/pretix/api/views/organizer.py @@ -394,6 +394,7 @@ class TeamViewSet(viewsets.ModelViewSet): ) return inst + @transaction.atomic() def perform_destroy(self, instance): instance.log_action('pretix.team.deleted', user=self.request.user, auth=self.request.auth) instance.delete() @@ -693,6 +694,7 @@ class MembershipTypeViewSet(viewsets.ModelViewSet): ctx['organizer'] = self.request.organizer return ctx + @transaction.atomic() def perform_destroy(self, instance): if not instance.allow_delete(): raise PermissionDenied("Can only be deleted if unused.") @@ -833,6 +835,7 @@ class SalesChannelViewSet(viewsets.ModelViewSet): ) return inst + @transaction.atomic() def perform_destroy(self, instance): if not instance.allow_delete(): raise PermissionDenied("Can only be deleted if unused.") diff --git a/src/pretix/api/views/waitinglist.py b/src/pretix/api/views/waitinglist.py index 569c64b56..c2fd0b30b 100644 --- a/src/pretix/api/views/waitinglist.py +++ b/src/pretix/api/views/waitinglist.py @@ -20,6 +20,7 @@ # . # import django_filters +from django.db import transaction from django_filters.rest_framework import DjangoFilterBackend, FilterSet from django_scopes import scopes_disabled from rest_framework import viewsets @@ -62,6 +63,7 @@ class WaitingListViewSet(viewsets.ModelViewSet): ctx['event'] = self.request.event return ctx + @transaction.atomic() def perform_create(self, serializer): serializer.save(event=self.request.event) serializer.instance.log_action( @@ -70,6 +72,7 @@ class WaitingListViewSet(viewsets.ModelViewSet): auth=self.request.auth, ) + @transaction.atomic() def perform_update(self, serializer): if serializer.instance.voucher: raise PermissionDenied('This entry can not be changed as it has already been assigned a voucher.') @@ -80,6 +83,7 @@ class WaitingListViewSet(viewsets.ModelViewSet): auth=self.request.auth, ) + @transaction.atomic() def perform_destroy(self, instance): if instance.voucher: raise PermissionDenied('This entry can not be deleted as it has already been assigned a voucher.') diff --git a/src/pretix/api/views/webhooks.py b/src/pretix/api/views/webhooks.py index b7b883906..d42c40c6b 100644 --- a/src/pretix/api/views/webhooks.py +++ b/src/pretix/api/views/webhooks.py @@ -20,6 +20,7 @@ # . # import django_filters +from django.db import transaction from django_filters.rest_framework import DjangoFilterBackend, FilterSet from rest_framework import viewsets @@ -48,6 +49,7 @@ class WebHookViewSet(viewsets.ModelViewSet): ctx['organizer'] = self.request.organizer return ctx + @transaction.atomic() def perform_create(self, serializer): inst = serializer.save(organizer=self.request.organizer) self.request.organizer.log_action( @@ -57,6 +59,7 @@ class WebHookViewSet(viewsets.ModelViewSet): data=merge_dicts(self.request.data, {'id': inst.pk}) ) + @transaction.atomic() def perform_update(self, serializer): inst = serializer.save(organizer=self.request.organizer) self.request.organizer.log_action( @@ -67,6 +70,7 @@ class WebHookViewSet(viewsets.ModelViewSet): ) return inst + @transaction.atomic() def perform_destroy(self, instance): self.request.organizer.log_action( 'pretix.webhook.changed', diff --git a/src/pretix/plugins/sendmail/api.py b/src/pretix/plugins/sendmail/api.py index 7d5e1548a..2e751e5ef 100644 --- a/src/pretix/plugins/sendmail/api.py +++ b/src/pretix/plugins/sendmail/api.py @@ -20,6 +20,7 @@ # . # from django.core.exceptions import ValidationError +from django.db import transaction from django_filters.rest_framework import DjangoFilterBackend, FilterSet from django_scopes import scopes_disabled from rest_framework import viewsets @@ -118,6 +119,7 @@ class RuleViewSet(viewsets.ModelViewSet): def get_queryset(self): return Rule.objects.filter(event=self.request.event) + @transaction.atomic() def perform_create(self, serializer): super().perform_create(serializer) serializer.instance.log_action( @@ -128,6 +130,7 @@ class RuleViewSet(viewsets.ModelViewSet): ) + @transaction.atomic() def perform_update(self, serializer): super().perform_update(serializer) serializer.instance.log_action( @@ -137,6 +140,7 @@ class RuleViewSet(viewsets.ModelViewSet): data=self.request.data ) + @transaction.atomic() def perform_destroy(self, instance): instance.log_action( 'pretix.plugins.sendmail.rule.deleted', -- 2.54.0 From ea792e76b252459444e5e8e80622fe862f16e23f Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Thu, 6 Aug 2026 09:07:20 +0200 Subject: [PATCH 023/249] Device list: Show online status (#6455) * Device list: Show online status * Fixes post-review --- src/pretix/base/models/devices.py | 7 +++++++ .../templates/pretixcontrol/organizers/devices.html | 5 +++++ src/pretix/control/views/organizer.py | 6 ++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/pretix/base/models/devices.py b/src/pretix/base/models/devices.py index 8997da3fe..108f3443b 100644 --- a/src/pretix/base/models/devices.py +++ b/src/pretix/base/models/devices.py @@ -20,11 +20,13 @@ # . # import string +from datetime import timedelta from django.core.exceptions import ValidationError from django.db import models from django.db.models import Max from django.utils.crypto import get_random_string +from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from django_scopes import ScopedManager, scopes_disabled @@ -307,3 +309,8 @@ class DeviceLastSeen(models.Model): autosummarize=True ) ] + + @property + def is_recent(self): + # pretixSCAN/pretixPOS sync every 5 minutes, so 7 minutes can be considered "offline" + return now() - self.last_seen < timedelta(minutes=7) diff --git a/src/pretix/control/templates/pretixcontrol/organizers/devices.html b/src/pretix/control/templates/pretixcontrol/organizers/devices.html index 6597b3cfe..726bf8e0c 100644 --- a/src/pretix/control/templates/pretixcontrol/organizers/devices.html +++ b/src/pretix/control/templates/pretixcontrol/organizers/devices.html @@ -118,6 +118,10 @@ {% endif %} + {{ d.device_id }} @@ -125,6 +129,7 @@ {% endif %} {{ d.name }} {% if d.revoked %}{% endif %} + {% if d.gate %}
{{ d.gate.name }} diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index ca6d5f11f..768fbdd22 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -108,7 +108,9 @@ from pretix.base.services.export import ( init_organizer_exporters, multiexport, scheduled_organizer_export, ) from pretix.base.services.mail import mail, prefix_subject -from pretix.base.services.placeholders import prepare_sample_context_for_preview +from pretix.base.services.placeholders import ( + prepare_sample_context_for_preview, +) from pretix.base.templatetags.rich_text import markdown_compile_email from pretix.base.views.tasks import AsyncAction from pretix.control.forms.exports import ScheduledOrganizerExportForm @@ -1206,7 +1208,7 @@ class DeviceQueryMixin: def get_queryset(self): qs = self.request.organizer.devices.prefetch_related( 'limit_events', 'gate', - ).order_by('revoked', '-device_id') + ).select_related('last_seen').order_by('revoked', '-device_id') if 'device' in self.request_data and '__ALL' not in self.request_data: qs = qs.filter( -- 2.54.0 From 4d9dfa88fedf1f7b976670902b7c43a1ba84c2a1 Mon Sep 17 00:00:00 2001 From: Lukas Bockstaller Date: Thu, 6 Aug 2026 15:06:20 +0200 Subject: [PATCH 024/249] Paypal2: handle incomming capture webhooks (Z#23240966) (#6456) * store the state of the payment regardless of the state control.html shows the banner that the payment is in review depending on payment.info * handle capture ressource * Update src/pretix/plugins/paypal2/views.py Co-authored-by: Phin Wolkwitz * add test * cleanup logic regarding uninteresting resource_type * store payment.info during _execute_payment asap --------- Co-authored-by: Phin Wolkwitz --- src/pretix/plugins/paypal2/payment.py | 2 ++ src/pretix/plugins/paypal2/views.py | 11 ++++++----- src/tests/plugins/paypal2/test_webhook.py | 6 ++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/pretix/plugins/paypal2/payment.py b/src/pretix/plugins/paypal2/payment.py index c279ce235..84f3edf60 100644 --- a/src/pretix/plugins/paypal2/payment.py +++ b/src/pretix/plugins/paypal2/payment.py @@ -677,6 +677,8 @@ class PaypalMethod(BasePaymentProvider): raise PaymentException(_('We had trouble communicating with PayPal')) else: pp_captured_order = response.result + payment.info = json.dumps(pp_captured_order.dict()) + payment.save() try: ReferencedPayPalObject.objects.get_or_create(order=payment.order, payment=payment, reference=pp_captured_order.id) diff --git a/src/pretix/plugins/paypal2/views.py b/src/pretix/plugins/paypal2/views.py index 56b118230..37340e22a 100644 --- a/src/pretix/plugins/paypal2/views.py +++ b/src/pretix/plugins/paypal2/views.py @@ -357,14 +357,13 @@ def webhook(request, *args, **kwargs): if 'resource_type' not in event_json: return HttpResponse("Invalid body, no resource_type given", status=400) - if event_json['resource_type'] not in ["checkout-order", "refund", "capture"]: - return HttpResponse("Not interested in this resource type", status=200) - # Retrieve the Charge ID of the refunded payment - if event_json['resource_type'] == 'refund': + if event_json['resource_type'] == 'checkout-order': + payloadid = event_json['resource']['id'] + elif event_json['resource_type'] == 'refund' or event_json['resource_type'] == 'capture': payloadid = get_link(event_json['resource']['links'], 'up')['href'].split('/')[-1] else: - payloadid = event_json['resource']['id'] + return HttpResponse("Not interested in this resource type", status=200) refs = [payloadid] if event_json['resource'].get('supplementary_data', {}).get('related_ids', {}).get('order_id'): @@ -424,6 +423,8 @@ def webhook(request, *args, **kwargs): **event_json, '_order_state': sale.dict(), }) + payment.info = json.dumps(sale.dict()) + payment.save() if payment.state == OrderPayment.PAYMENT_STATE_CONFIRMED and sale['status'] in ('PARTIALLY_REFUNDED', 'REFUNDED', 'COMPLETED'): if event_json['resource_type'] == 'refund': diff --git a/src/tests/plugins/paypal2/test_webhook.py b/src/tests/plugins/paypal2/test_webhook.py index b1355018c..21eb88853 100644 --- a/src/tests/plugins/paypal2/test_webhook.py +++ b/src/tests/plugins/paypal2/test_webhook.py @@ -22,6 +22,7 @@ import json from datetime import timedelta from decimal import Decimal +from unittest.mock import MagicMock import pytest from django.utils.timezone import now @@ -409,7 +410,8 @@ def test_webhook_mark_paid(env, client, monkeypatch): order.payments.update(state=OrderPayment.PAYMENT_STATE_PENDING) pp_order = Result(get_test_order()) - monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", lambda *args: pp_order) + mock_orders_get_request = MagicMock(return_value=pp_order) + monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", mock_orders_get_request) monkeypatch.setattr("pretix.plugins.paypal2.payment.PaypalMethod.init_api", init_api) with scopes_disabled(): ReferencedPayPalObject.objects.create(order=order, payment=order.payments.first(), @@ -497,7 +499,7 @@ def test_webhook_mark_paid(env, client, monkeypatch): "resource_version": "2.0" } ), content_type='application_json') - + mock_orders_get_request.assert_called_once_with('806440346Y391300T') order.refresh_from_db() assert order.status == Order.STATUS_PAID -- 2.54.0 From 81f58456e5b6a4823d7f01e2b7890084b5200fc3 Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Fri, 7 Aug 2026 11:03:36 +0200 Subject: [PATCH 025/249] Fix API-docs example for addon_to on order-change (#6451) * Fix API-docs example for addon_to on order-change * Update orders.rst --- doc/api/resources/orders.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/resources/orders.rst b/doc/api/resources/orders.rst index f14fd1b1b..2ab607908 100644 --- a/doc/api/resources/orders.rst +++ b/doc/api/resources/orders.rst @@ -2038,7 +2038,7 @@ Manipulating individual positions * ``order`` (mandatory, specified as a string mapping to a ``code``) - * ``addon_to`` (optional, specified as an integer mapping to the ``positionid`` of the parent position) + * ``addon_to`` (optional, specified as an integer mapping to ``positionid`` - the number of the position within the order, see :ref:`_order-position-resource` - of the parent position) * ``item`` (mandatory) @@ -2348,7 +2348,7 @@ otherwise, such as splitting an order or changing fees. "subevent": 562, "seat": "seat-guid-2", "price": "99.99", - "addon_to": 12374, + "addon_to": 1, "attendee_name": "Peter", } ], -- 2.54.0 From 958f75b109fef69fc503e805cca25e186342909d Mon Sep 17 00:00:00 2001 From: luelista Date: Fri, 7 Aug 2026 14:36:46 +0200 Subject: [PATCH 026/249] Add tests to prevent reintroducing CSP nonces (Z#23240534) (#6409) As discussed in PR #6387 --- src/tests/control/test_views.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/tests/control/test_views.py b/src/tests/control/test_views.py index f169df702..1560affe1 100644 --- a/src/tests/control/test_views.py +++ b/src/tests/control/test_views.py @@ -224,3 +224,28 @@ def test_one_view(logged_in_client, url, expected, event, item, item_category, o ) response = logged_in_client.get(url) assert response.status_code == expected + + # Do not reintroduce any CSP nonces into control responses, as discussed in PR #6387 + if response['Content-Type'] != 'application/json': + assert 'script-src' in response['Content-Security-Policy'] + assert 'nonce-' not in response['Content-Security-Policy'] + + +@pytest.mark.parametrize('url', [ + '/control/login', + '/', + '/{orga}/{event}/', +]) +@pytest.mark.django_db +def test_csp_header_unauthenticated(client, url, event): + # Do not reintroduce any CSP nonces into most presale responses, as discussed in PR #6387 + with scope(organizer=event.organizer): + url = url.format( + event=event.slug, orga=event.organizer.slug, + ) + event.live = True + event.save() + response = client.get(url) + assert response.status_code == 200 + assert 'script-src' in response['Content-Security-Policy'] + assert 'nonce-' not in response['Content-Security-Policy'] -- 2.54.0 From 4a286896906776a335a67d57e0d3ed86c014e7fc Mon Sep 17 00:00:00 2001 From: Lukas Bockstaller Date: Fri, 7 Aug 2026 16:34:36 +0200 Subject: [PATCH 027/249] log paypal payment durations (#6461) * log payment processing durations * remove log noise * fix attribute access * remove debugging import --- src/pretix/plugins/paypal2/payment.py | 19 +++++++++++++++++-- src/pretix/plugins/paypal2/views.py | 1 + 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/pretix/plugins/paypal2/payment.py b/src/pretix/plugins/paypal2/payment.py index 84f3edf60..31634ce00 100644 --- a/src/pretix/plugins/paypal2/payment.py +++ b/src/pretix/plugins/paypal2/payment.py @@ -23,7 +23,7 @@ import json import logging import urllib.parse from collections import OrderedDict -from datetime import timedelta +from datetime import datetime, timedelta from decimal import Decimal from django import forms @@ -645,7 +645,7 @@ class PaypalMethod(BasePaymentProvider): def _execute_payment(self, request: HttpRequest, payment: OrderPayment): payment = OrderPayment.objects.select_for_update(of=OF_SELF).get(pk=payment.pk) if payment.state == OrderPayment.PAYMENT_STATE_CONFIRMED: - logger.warning('payment is already confirmed; possible return-view/webhook race-condition') + # payment is already confirmed; possible return-view/webhook race-condition return try: @@ -832,6 +832,7 @@ class PaypalMethod(BasePaymentProvider): payment.info = json.dumps(pp_captured_order.dict()) payment.save(update_fields=['info']) payment.confirm() + self.log_payment_duration(payment) except Quota.QuotaExceededException as e: raise PaymentException(str(e)) # Payment has not any captures yet - so it's probably in created status @@ -841,6 +842,20 @@ class PaypalMethod(BasePaymentProvider): if 'payment_paypal_oid' in request.session: del request.session['payment_paypal_oid'] + @staticmethod + def log_payment_duration(payment: OrderPayment): + try: + capture = payment.info_data["purchase_units"][0]["payments"]["captures"][0] + create_time: str | None = capture["create_time"] + update_time: str | None = capture["update_time"] + except (KeyError, IndexError, TypeError): + create_time = None + update_time = None + + if create_time is not None and update_time is not None: + duration = datetime.fromisoformat(update_time) - datetime.fromisoformat(create_time) + logger.info('{}: {} - paypal payment processing time'.format(str(payment.global_id), str(duration))) + def payment_pending_render(self, request, payment) -> str: retry = True try: diff --git a/src/pretix/plugins/paypal2/views.py b/src/pretix/plugins/paypal2/views.py index 37340e22a..7a125e459 100644 --- a/src/pretix/plugins/paypal2/views.py +++ b/src/pretix/plugins/paypal2/views.py @@ -490,6 +490,7 @@ def webhook(request, *args, **kwargs): payment.info = json.dumps(sale.dict()) payment.save(update_fields=['info']) payment.confirm() + prov.log_payment_duration(payment) except Quota.QuotaExceededException: pass elif sale['status'] == 'APPROVED': -- 2.54.0 From d08216d8c5fddad9ddded21e21cc9271dc8f8146 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Fri, 7 Aug 2026 18:59:01 +0200 Subject: [PATCH 028/249] API: Allow to simulate check-ins (#6360) * API: Allow to simulate check-ins * Add missing file --- doc/api/resources/checkin.rst | 2 ++ src/pretix/api/serializers/checkin.py | 1 + src/pretix/api/views/checkin.py | 6 ++++ src/pretix/base/services/checkin.py | 7 +++-- src/pretix/helpers/database.py | 9 ++++++ src/pretix/testutils/db.py | 25 +++++++++++++++++ src/tests/api/test_checkinrpc.py | 40 +++++++++++++++++++++++++++ 7 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 src/pretix/testutils/db.py diff --git a/doc/api/resources/checkin.rst b/doc/api/resources/checkin.rst index c4571ade2..cb3fc8e04 100644 --- a/doc/api/resources/checkin.rst +++ b/doc/api/resources/checkin.rst @@ -71,6 +71,8 @@ Checking a ticket in :>json object questions: List of questions to be answered for check-in, only set on status ``"incomplete"``. :>json object media_policy: Reusable media policy (see documentation on items), only set on status ``"exchange"``. :>json object media_type: Reusable media type (see documentation on items), only set on status ``"exchange"``. + :>json boolean simulate: Do not actually perform the check-in, only simulate the response. The ``position`` response + object will not reflect the simulated changes. **Example request**: diff --git a/src/pretix/api/serializers/checkin.py b/src/pretix/api/serializers/checkin.py index db716a315..62e095c3f 100644 --- a/src/pretix/api/serializers/checkin.py +++ b/src/pretix/api/serializers/checkin.py @@ -90,6 +90,7 @@ class CheckinRPCRedeemInputSerializer(serializers.Serializer): answers = serializers.JSONField(required=False, allow_null=True) exchange_medium_type = serializers.ChoiceField(required=False, choices=MEDIA_TYPES) exchange_medium_identifier = serializers.CharField(required=False) + simulate = serializers.BooleanField(default=False, required=False) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/src/pretix/api/views/checkin.py b/src/pretix/api/views/checkin.py index 1eb05b494..87477dd82 100644 --- a/src/pretix/api/views/checkin.py +++ b/src/pretix/api/views/checkin.py @@ -839,6 +839,11 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force, ) if exchange_medium_identifier: # other fields are filled, see CheckinRPCRedeemInputSerializer.validate + if simulate: + raise CheckInError( + gettext('You cannot simulate a medium exchange.'), + 'error' + ) with transaction.atomic(): # Do exchange and check-in atomically, i.e. both succeed or both fail medium = perform_media_exchange( @@ -1066,6 +1071,7 @@ class CheckinRPCRedeemView(views.APIView): legacy_url_support=False, exchange_medium_type=s.validated_data.get('exchange_medium_type'), exchange_medium_identifier=s.validated_data.get('exchange_medium_identifier'), + simulate=s.validated_data.get('simulate'), ) diff --git a/src/pretix/base/services/checkin.py b/src/pretix/base/services/checkin.py index 3ac7b6792..039f438d4 100644 --- a/src/pretix/base/services/checkin.py +++ b/src/pretix/base/services/checkin.py @@ -40,7 +40,7 @@ import dateutil import dateutil.parser from dateutil.tz import datetime_exists from django.core.files import File -from django.db import IntegrityError, transaction +from django.db import IntegrityError from django.db.models import ( BooleanField, Case, Count, ExpressionWrapper, F, IntegerField, Max, Min, OuterRef, Q, Subquery, TextField, Value, When, @@ -59,6 +59,7 @@ from pretix.base.models import ( ) from pretix.base.signals import checkin_created, periodic_task from pretix.helpers import OF_SELF +from pretix.helpers.database import conditional_atomic from pretix.helpers.jsonlogic import Logic from pretix.helpers.jsonlogic_boolalg import convert_to_dnf from pretix.helpers.jsonlogic_query import ( @@ -1043,10 +1044,10 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict, if not simulate: _save_answers(op, answers, given_answers) - with transaction.atomic(): + with conditional_atomic(not simulate): # Lock order positions, if it is an entry. We don't need it for exits, as a race condition wouldn't be problematic opqs = OrderPosition.all.select_related("order", "item") - if type != Checkin.TYPE_EXIT: + if type != Checkin.TYPE_EXIT and not simulate: opqs = opqs.select_for_update(of=OF_SELF) op = opqs.get(pk=op.pk) diff --git a/src/pretix/helpers/database.py b/src/pretix/helpers/database.py index 06360cb05..931ba35ee 100644 --- a/src/pretix/helpers/database.py +++ b/src/pretix/helpers/database.py @@ -288,6 +288,15 @@ def get_deterministic_ordering(model, ordering): return ordering +@contextlib.contextmanager +def conditional_atomic(do_atomic, **kwargs): + if do_atomic: + with transaction.atomic(**kwargs): + yield + else: + yield + + class IgnoreOnSQLiteMixin: # Mixin to allow defining PostgreSQL-specific indexes that will just not be created # on SQLite. SQLite is supported for testing only anyways! diff --git a/src/pretix/testutils/db.py b/src/pretix/testutils/db.py new file mode 100644 index 000000000..50692a48d --- /dev/null +++ b/src/pretix/testutils/db.py @@ -0,0 +1,25 @@ +# +# This file is part of pretix (Community Edition). +# +# Copyright (C) 2014-2020 Raphael Michel and contributors +# Copyright (C) 2020-today pretix GmbH and contributors +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General +# Public License as published by the Free Software Foundation in version 3 of the License. +# +# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are +# applicable granting you additional permissions and placing additional restrictions on your usage of this software. +# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive +# this file, see . +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along with this program. If not, see +# . +# +def readonly_db(execute, sql, params, many, context): + if not sql.lower().startswith("select"): + raise Exception(f"Should not write anything to the database, but detected query: {sql}") + return execute(sql, params, many, context) diff --git a/src/tests/api/test_checkinrpc.py b/src/tests/api/test_checkinrpc.py index 6ea5066cb..da27c78ad 100644 --- a/src/tests/api/test_checkinrpc.py +++ b/src/tests/api/test_checkinrpc.py @@ -25,6 +25,7 @@ from unittest import mock import pytest from django.core.files.base import ContentFile +from django.db import connection from django.utils.timezone import now from django_countries.fields import Country from django_scopes import scopes_disabled @@ -36,6 +37,7 @@ from pretix.api.serializers.item import QuestionSerializer from pretix.base.models import ( Checkin, InvoiceAddress, Item, Order, OrderPosition, ReusableMedium, ) +from pretix.testutils.db import readonly_db # Lots of this code is overlapping with test_checkin.py, and some of it is arguably redundant since it's triggering # the same backend code paths (for now). However, this is SUCH a critical part of pretix that we don't want to take @@ -1739,3 +1741,41 @@ def test_exchange_create_gift_card(token_client, organizer, clist, event, order, with scopes_disabled(): rm = ReusableMedium.objects.get(identifier="0412345") assert rm.linked_giftcard.currency == "EUR" + + +@pytest.mark.django_db +def test_simulate(token_client, organizer, clist, event, order): + with scopes_disabled(): + p = order.positions.first() + with connection.execute_wrapper(readonly_db): + resp = _redeem(token_client, organizer, clist, p.secret, {"simulate": True}) + assert resp.status_code == 201 + assert resp.data['status'] == 'ok' + with scopes_disabled(): + assert not p.checkins.exists() + + +@pytest.mark.django_db +def test_simulate_no_exchange(token_client, organizer, clist, event, order, item): + organizer.settings.reusable_media_type_nfc_uid = True + item.media_type = "nfc_uid" + item.media_policy = Item.MEDIA_POLICY_NEW + item.save() + with scopes_disabled(): + rm = ReusableMedium.objects.create( + type="nfc_uid", + identifier="12345678", + organizer=organizer, + ) + with connection.execute_wrapper(readonly_db): + resp = _redeem(token_client, organizer, clist, "z3fsn8jyufm5kpk768q69gkbyr5f4h6w", { + "source_type": "barcode", + "exchange_medium_type": "nfc_uid", + "exchange_medium_identifier": "12345678", + "simulate": True, + }) + assert resp.status_code == 400 + assert resp.data['status'] == 'error' + assert resp.data['reason'] == 'error' + with scopes_disabled(): + assert not rm.linked_orderpositions.exists() -- 2.54.0 From 5cf28f1b815de5f836b7549ca222b72fa3e53fd3 Mon Sep 17 00:00:00 2001 From: robbi5 Date: Mon, 10 Aug 2026 15:58:15 +0200 Subject: [PATCH 029/249] Fix typo in devicesecurity blocking checkinrpc.annul (#6467) --- src/pretix/api/auth/devicesecurity.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pretix/api/auth/devicesecurity.py b/src/pretix/api/auth/devicesecurity.py index b959c0a7d..8ef6d0218 100644 --- a/src/pretix/api/auth/devicesecurity.py +++ b/src/pretix/api/auth/devicesecurity.py @@ -115,7 +115,7 @@ class PretixScanSecurityProfile(AllowListSecurityProfile): ('GET', 'api-v1:event.settings'), ('POST', 'api-v1:upload'), ('POST', 'api-v1:checkinrpc.redeem'), - ('POST', 'api-v1:checkinrpc.annull'), + ('POST', 'api-v1:checkinrpc.annul'), ('GET', 'api-v1:checkinrpc.search'), ('GET', 'api-v1:reusablemedium-list'), ('POST', 'api-v1:reusablemedium-lookup'), @@ -154,7 +154,7 @@ class PretixScanNoSyncNoSearchSecurityProfile(AllowListSecurityProfile): ('GET', 'api-v1:event.settings'), ('POST', 'api-v1:upload'), ('POST', 'api-v1:checkinrpc.redeem'), - ('POST', 'api-v1:checkinrpc.annull'), + ('POST', 'api-v1:checkinrpc.annul'), ('GET', 'api-v1:checkinrpc.search'), ) @@ -191,7 +191,7 @@ class PretixScanNoSyncSecurityProfile(AllowListSecurityProfile): ('GET', 'api-v1:event.settings'), ('POST', 'api-v1:upload'), ('POST', 'api-v1:checkinrpc.redeem'), - ('POST', 'api-v1:checkinrpc.annull'), + ('POST', 'api-v1:checkinrpc.annul'), ('GET', 'api-v1:checkinrpc.search'), ) -- 2.54.0 From f25c233e919b8fb3a8ddf88a4e387bd9aebd58a9 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 10 Aug 2026 16:13:54 +0200 Subject: [PATCH 030/249] Fix performance issues in download reminder (#6393) * Fix performance issues in download reminder * Update src/pretix/base/services/orders.py Co-authored-by: Richard Schreiber * Fixes after review * Fix check in loop --------- Co-authored-by: Richard Schreiber --- src/pretix/base/services/orders.py | 163 ++++++++++++++++------------- src/pretix/base/settings.py | 2 +- 2 files changed, 93 insertions(+), 72 deletions(-) diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index 23b21c9be..17a66924b 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -48,12 +48,12 @@ from celery.exceptions import MaxRetriesExceededError from django.conf import settings from django.core.cache import cache from django.core.exceptions import ValidationError -from django.db import transaction +from django.db import models, transaction from django.db.models import ( - Count, Exists, F, IntegerField, Max, Min, OuterRef, Q, QuerySet, Sum, - Value, + Count, Exists, F, IntegerField, Max, Min, OuterRef, Q, QuerySet, Subquery, + Sum, Value, ) -from django.db.models.functions import Coalesce, Greatest +from django.db.models.functions import Cast, Greatest from django.db.transaction import get_connection from django.dispatch import receiver from django.utils.functional import cached_property @@ -71,7 +71,7 @@ from pretix.base.models import ( Membership, Order, OrderPayment, OrderPosition, Quota, Seat, SeatCategoryMapping, User, Voucher, ) -from pretix.base.models.event import SubEvent +from pretix.base.models.event import Event_SettingsStore, SubEvent from pretix.base.models.orders import ( BlockedTicketSecret, InvoiceAddress, OrderFee, OrderRefund, generate_secret, @@ -1494,83 +1494,104 @@ def send_expiry_warnings(sender, **kwargs): @scopes_disabled() def send_download_reminders(sender, **kwargs): today = now().replace(hour=0, minute=0, second=0, microsecond=0) - qs = Order.objects.annotate( - first_date=Coalesce( - Min('all_positions__subevent__date_from'), - F('event__date_from') + + events = Event.objects.filter( + Q(has_subevents=False, date_from__gte=now()) | + (Q(has_subevents=True) & Q(Exists( + SubEvent.objects.filter(event_id=OuterRef('id'), date_from__gte=now()) + ))) + ).annotate( + reminder_days=Subquery( + Event_SettingsStore.objects.filter( + object=OuterRef('id'), + key='mail_days_download_reminder' + ).exclude( + value="None" + ).annotate( + val=Cast(F("value"), output_field=models.IntegerField()), + ).values("val") ) ).filter( - download_reminder_sent=False, - datetime__lte=now() - timedelta(hours=2), - first_date__gte=today, - ).only( - 'pk', 'event_id', 'sales_channel', 'datetime', - ).order_by('event_id') - event_id = None - days = None - event = None + reminder_days__isnull=False, + ).order_by() - for o in qs: - if o.event_id != event_id: - days = o.event.settings.get('mail_days_download_reminder', as_type=int) - event = o.event - event_id = o.event_id + for event in events.iterator(chunk_size=10_000): + qs = event.orders.filter( + download_reminder_sent=False, + datetime__lte=now() - timedelta(hours=2), + ) - if days is None: - continue - - if o.sales_channel.identifier not in event.settings.mail_sales_channel_download_reminder: - continue - - reminder_date = (o.first_date - timedelta(days=days)).replace(hour=0, minute=0, second=0, microsecond=0) - if now() < reminder_date or o.datetime > reminder_date: - continue - - with transaction.atomic(): - o = Order.objects.select_for_update(of=OF_SELF).get(pk=o.pk) - if o.download_reminder_sent: - # Race condition - continue - positions = list(o.positions_with_tickets) - if not positions: + if event.has_subevents: + qs = qs.annotate( + first_date=Min('all_positions__subevent__date_from') + ).filter( + Q(first_date__gte=today) + ) + else: + event_reminder_date = (event.date_from - timedelta(days=event.reminder_days)).replace(hour=0, minute=0, second=0, microsecond=0) + if now() < event_reminder_date: continue - if not o.ticket_download_available: + qs = qs.only( + 'pk', 'event_id', 'sales_channel', 'datetime', + ).order_by() + + for o in qs: + if o.sales_channel.identifier not in event.settings.mail_sales_channel_download_reminder: continue - if o.status != Order.STATUS_PAID: - if o.status != Order.STATUS_PENDING or o.require_approval or (not o.valid_if_pending and not o.event.settings.ticket_download_pending): + if event.has_subevents: + reminder_date = ((o.first_date or event.date_from) - timedelta(days=event.reminder_days)).replace(hour=0, minute=0, second=0, microsecond=0) + else: + reminder_date = event_reminder_date + if now() < reminder_date or o.datetime > reminder_date: + continue + + with transaction.atomic(): + o = Order.objects.select_for_update(of=OF_SELF).get(pk=o.pk) + if o.download_reminder_sent: + # Race condition + continue + positions = list(o.positions_with_tickets) + if not positions: continue - with language(o.locale, o.event.settings.region): - o.download_reminder_sent = True - o.save(update_fields=['download_reminder_sent']) - email_template = event.settings.mail_text_download_reminder - email_subject = event.settings.mail_subject_download_reminder - email_context = get_email_context(event=event, order=o) - o.send_mail( - email_subject, email_template, email_context, - 'pretix.event.order.email.download_reminder_sent', - attach_tickets=True - ) + if not o.ticket_download_available: + continue - if event.settings.mail_send_download_reminder_attendee: - for p in positions: - if p.subevent_id: - reminder_date = (p.subevent.date_from - timedelta(days=days)).replace( - hour=0, minute=0, second=0, microsecond=0 - ) - if now() < reminder_date: - continue - if p.addon_to_id is None and p.attendee_email and p.attendee_email != o.email: - email_template = event.settings.mail_text_download_reminder_attendee - email_subject = event.settings.mail_subject_download_reminder_attendee - email_context = get_email_context(event=event, order=o, position=p) - o.send_mail( - email_subject, email_template, email_context, - 'pretix.event.order.email.download_reminder_sent', - attach_tickets=True, position=p - ) + if o.status != Order.STATUS_PAID: + if o.status != Order.STATUS_PENDING or o.require_approval or (not o.valid_if_pending and not o.event.settings.ticket_download_pending): + continue + + with language(o.locale, o.event.settings.region): + o.download_reminder_sent = True + o.save(update_fields=['download_reminder_sent']) + email_template = event.settings.mail_text_download_reminder + email_subject = event.settings.mail_subject_download_reminder + email_context = get_email_context(event=event, order=o) + o.send_mail( + email_subject, email_template, email_context, + 'pretix.event.order.email.download_reminder_sent', + attach_tickets=True + ) + + if event.settings.mail_send_download_reminder_attendee: + for p in positions: + if p.subevent_id: + reminder_date = (p.subevent.date_from - timedelta(days=event.reminder_days)).replace( + hour=0, minute=0, second=0, microsecond=0 + ) + if now() < reminder_date: + continue + if p.addon_to_id is None and p.attendee_email and p.attendee_email != o.email: + email_template = event.settings.mail_text_download_reminder_attendee + email_subject = event.settings.mail_subject_download_reminder_attendee + email_context = get_email_context(event=event, order=o, position=p) + o.send_mail( + email_subject, email_template, email_context, + 'pretix.event.order.email.download_reminder_sent', + attach_tickets=True, position=p + ) def notify_user_changed_order(order, user=None, auth=None, invoices=[]): diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index ae679d186..bdf881359 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -2930,7 +2930,7 @@ Your {event} team""")) # noqa: W291 }, 'mail_days_download_reminder': { 'type': int, - 'default': None + 'default': None # when this default is changed, send_download_reminders needs to change }, 'mail_send_download_reminder_attendee': { 'type': bool, -- 2.54.0 From 7fe31634e62baf62584570fc04097209d7b1e0d4 Mon Sep 17 00:00:00 2001 From: Lukas Bockstaller Date: Wed, 12 Aug 2026 15:37:29 +0200 Subject: [PATCH 031/249] Allow RelativeDate in relation to a moment of order (#6160) * initial implementation * factor out _resolve_base_date * add js to prevent illegal inputs * fix tests * Update src/pretix/base/reldate.py Co-authored-by: Raphael Michel * Apply suggestions from code review Co-authored-by: Raphael Michel * move js includes of to fragment_js.html * add type annotations * moves logic from RelativeDateWrapper into RelativeDate and adds BaseChoice for configuring which models attributes support which relationship * fix tests * test upgrade behaviour * Apply suggestions from code review Co-authored-by: Raphael Michel * move reldate.js include in correct file * add OptionAttrsSelect to allow select options with their own attributes per value * add a little bit of information to the tests for future reference * rewrite reldate.js use data-attributes * general cleanup * add test for order.subevents cases * use correct choice format * remove order.subevent variants * various cleanup * Apply suggestions from code review Co-authored-by: Raphael Michel * remove empty docstrings * add depreciation warning * change event listener to pretix:bind-forms --------- Co-authored-by: Raphael Michel Co-authored-by: Raphael Michel --- src/pretix/base/forms/widgets.py | 15 + src/pretix/base/reldate.py | 528 +++++++++++++----- src/pretix/control/forms/subevents.py | 12 +- .../control/templates/pretixcontrol/base.html | 1 + src/pretix/control/views/subevents.py | 2 +- src/pretix/static/pretixbase/js/reldate.js | 42 ++ src/tests/base/test_models.py | 28 +- src/tests/base/test_orders.py | 4 +- src/tests/base/test_payment.py | 12 +- src/tests/base/test_reldate.py | 130 +++-- src/tests/base/test_waitinglist.py | 2 +- src/tests/control/test_events.py | 4 +- src/tests/control/test_subevents.py | 56 +- src/tests/presale/test_event.py | 2 +- src/tests/presale/test_order_change.py | 2 +- src/tests/presale/test_orders.py | 2 +- 16 files changed, 588 insertions(+), 254 deletions(-) create mode 100644 src/pretix/static/pretixbase/js/reldate.js diff --git a/src/pretix/base/forms/widgets.py b/src/pretix/base/forms/widgets.py index 598527a16..ccca7a816 100644 --- a/src/pretix/base/forms/widgets.py +++ b/src/pretix/base/forms/widgets.py @@ -314,3 +314,18 @@ class BusinessBooleanRadio(forms.RadioSelect): 'False': False, False: False, }.get(value) + + +class OptionAttrsSelect(forms.Select): + def __init__(self, *args, option_attrs=None, **kwargs): + super().__init__(*args, **kwargs) + self.option_attrs = option_attrs or {} + + def create_option(self, name, value, label, selected, index, subindex=None, attrs=None): + option = super().create_option( + name, value, label, selected, index, subindex=subindex, attrs=attrs + ) + extra = self.option_attrs.get(str(value)) + if extra: + option["attrs"].update(extra) + return option diff --git a/src/pretix/base/reldate.py b/src/pretix/base/reldate.py index 3b67610d5..510a4b466 100644 --- a/src/pretix/base/reldate.py +++ b/src/pretix/base/reldate.py @@ -19,30 +19,245 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # +import copy import datetime +import os +import warnings from collections import namedtuple -from typing import Union +from dataclasses import dataclass +from typing import ( + TYPE_CHECKING, Iterable, List, Literal, Optional, Tuple, Union, cast, +) from zoneinfo import ZoneInfo from dateutil import parser from django import forms from django.core.exceptions import ValidationError from django.db import models +from django.forms.widgets import Widget from django.utils.formats import get_format -from django.utils.functional import lazy +from django.utils.functional import Promise, lazy from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -BASE_CHOICES = ( - ('date_from', _('Event start')), - ('date_to', _('Event end')), - ('date_admission', _('Event admission')), - ('presale_start', _('Presale start')), - ('presale_end', _('Presale end')), -) +from pretix.base.forms.widgets import OptionAttrsSelect -RelativeDate = namedtuple('RelativeDate', ['days', 'minutes', 'time', 'is_after', 'base_date_name'], defaults=(0, None, None, False, 'date_from')) +if TYPE_CHECKING: + from .models import Event, Order, SubEvent + + +@dataclass(frozen=True) +class BaseChoice: + base: Literal["event", "order"] + attribute: str + text: Promise + supports_before: bool + supports_after: bool + + @property + def key(self) -> str: + key = f"{self.base}__{self.attribute}" + return key + + @staticmethod + def find(objects: Iterable["BaseChoice"], key: str) -> "BaseChoice": + if "__" in key: + choice = next((obj for obj in objects if obj.key == key), None) + else: + # fallback for RelativeDateFields stored, before support for bases other than event was added + choice = next((obj for obj in objects if obj.attribute == key and obj.base == "event"), None) + + if choice is None: + raise TypeError(f"key {key} must be a valid key in BASE_CHOICES") + + return choice + + +BASE_CHOICES: List[BaseChoice] = [ + BaseChoice('event', 'date_from', _('Event start'), True, True), + BaseChoice('event', 'date_to', _('Event end'), True, True), + BaseChoice('event', 'date_admission', _('Event admission'), True, True), + BaseChoice('event', 'presale_start', _('Presale start'), True, True), + BaseChoice('event', 'presale_end', _('Presale end'), True, True), + BaseChoice('order', 'datetime', _('Order creation'), False, True), + BaseChoice('order', 'expires', _('Order expiry'), True, True), +] + +EVENT_BASE_CHOICES = [ + x for x in BASE_CHOICES if x.base == 'event' +] + +ORDER_BASE_CHOICES = [ + x for x in BASE_CHOICES if x.base == 'order' +] + + +@dataclass(frozen=True) +class RelativeDate: + """ + This contains information on a date that is defined in relation to a fixed base point. + This means that the underlying data is a fixed date as the base point and a number of days or a time interval + to calculate the date. + + The list of valid base date choices is defined in BASE_CHOICES. + If the base_date_name is not set, the date_from attribute of Event is used. + """ + + days: int = 0 + minutes: Optional[int] = None + time: Optional[datetime.time] = None + is_after: bool = False + base_date_name: str = 'event__date_from__' + + def __post_init__(self) -> None: + if self.is_after and not self._choice.supports_after: + raise ValueError( + "The selected base date and attribute combination does not support relative dates placed after the base date" + ) + if not self.is_after and not self._choice.supports_before: + raise ValueError( + "The selected base date and attribute combination does not support relative dates placed before the base date") + + @property + def _choice(self): + return BaseChoice.find(BASE_CHOICES, self.base_date_name) + + @property + def key(self): + return self._choice.key + + def __eq__(self, o: object) -> bool: + if not isinstance(o, RelativeDate): + return False + return self.to_string() == o.to_string() + + def _resolve_date(self, base: "Event | Order | SubEvent") -> Tuple[datetime.datetime, ZoneInfo]: + """ + Resolves the datetime and timezone information of this RelativeDate object in relation to the provided reference. + + :param base: the reference which should be used to resolve the relative date + :return: + """ + from .models import Event, Order, SubEvent + + choice = self._choice + + if choice.base == "order" and isinstance(base, Order): + event = base.event + base_date = getattr(base, choice.attribute) + elif choice.base == "event" and isinstance(base, SubEvent): + event = base.event + base_date = (getattr(base, choice.attribute) or + getattr(base.event, choice.attribute) or + base.date_from) + elif choice.base == "event" and isinstance(base, Event): + event = base + base_date = getattr(base, choice.attribute) or event.date_from + else: + raise TypeError("The base defined by data does not match the passed in base") + + tz = ZoneInfo(event.settings.timezone) + return base_date, tz + + def date(self, base: "Event | Order | SubEvent") -> datetime.date: + """ + Resolves the effective date of this RelativeDate object in relation to the provided reference. + + :param base: the reference which should be used to resolve the date + :return: datetime.date + """ + if self.minutes is not None: + raise ValueError('A minute-based relative datetime can not be used as a date') + + base_date, tz = self._resolve_date(base) + + if self.is_after: + new_date = base_date.astimezone(tz) + datetime.timedelta(days=self.days) + else: + new_date = base_date.astimezone(tz) - datetime.timedelta(days=self.days) + return new_date.date() + + def datetime(self, base: "Event | Order | SubEvent") -> datetime.datetime: + """ + Resolves the effective datetime of this RelativeDate object in relation to the provided reference. + + :param base: the reference which should be used to resolve the datetime + :return: datetime.datetime + """ + base_date, tz = self._resolve_date(base) + + if self.minutes is not None: + if self.is_after: + return base_date.astimezone(tz) + datetime.timedelta(minutes=self.minutes) + else: + return base_date.astimezone(tz) - datetime.timedelta(minutes=self.minutes) + else: + if self.is_after: + new_date = (base_date.astimezone(tz) + datetime.timedelta(days=self.days)).astimezone(tz) + else: + new_date = (base_date.astimezone(tz) - datetime.timedelta(days=self.days)).astimezone(tz) + if self.time: + new_date = new_date.replace( + hour=self.time.hour, + minute=self.time.minute, + second=self.time.second + ) + new_date = new_date.astimezone(tz) + return new_date + + def to_string(self) -> str: + if self.minutes is not None: + return 'RELDATE/minutes/{}/{}/{}'.format( # + self.minutes, + self._choice.key, + 'after' if self.is_after else '', + ) + return 'RELDATE/{}/{}/{}/{}'.format( # + self.days, + self.time.strftime('%H:%M:%S') if self.time else '-', + self._choice.key, + 'after' if self.is_after else '', + ) + + @classmethod + def from_string(cls, input: str): + if not input.startswith('RELDATE/'): + raise TypeError("Invalid input for RelativeDate.from_string()") + + parts = input.split('/') + if parts[1] == 'minutes': + data = RelativeDate( + days=0, + minutes=int(parts[2]), + base_date_name=parts[3], + time=None, + is_after=len(parts) > 4 and parts[4] == "after", + ) + else: + if parts[2] == '-': + time = None + else: + timeparts = parts[2].split(':') + time = datetime.time(hour=int(timeparts[0]), minute=int(timeparts[1]), second=int(timeparts[2])) + try: + data = RelativeDate( + days=int(parts[1] or 0), + base_date_name=parts[3], + time=time, + minutes=None, + is_after=len(parts) > 4 and parts[4] == "after", + ) + except ValueError: + data = RelativeDate( + days=0, + base_date_name=parts[3], + time=time, + minutes=None, + is_after=len(parts) > 4 and parts[4] == "after", + ) + + return data class RelativeDateWrapper: @@ -50,130 +265,53 @@ class RelativeDateWrapper: This contains information on a date that might be relative to an event. This means that the underlying data is either a fixed date or a number of days and a wall clock time to calculate the date based on a base point. - - The base point can be the date_from, date_to, date_admission, presale_start or presale_end - attribute of an event or subevent. If the respective attribute is not set, ``date_from`` - will be used. """ def __init__(self, data: Union[datetime.datetime, RelativeDate]): self.data = data - def date(self, event) -> datetime.date: - from .models import SubEvent + def date(self, base: "Event | Order | SubEvent") -> datetime.date: + """ + If the RelativeDateWrapper wraps a RelativeDate object: + Resolves the effective date of this object in relation to the provided reference. + If the RelativeDateWrapper wraps an absolute date or datetime: + Returns the wrapped absolute date. + :param base: the reference which should be used to resolve the date in case it is a relative date. + :return: datetime.date + """ if isinstance(self.data, datetime.datetime): return self.data.date() elif isinstance(self.data, datetime.date): return self.data else: - if self.data.minutes is not None: - raise ValueError('A minute-based relative datetime can not be used as a date') + return self.data.date(base) - tz = ZoneInfo(event.settings.timezone) - if isinstance(event, SubEvent): - base_date = ( - getattr(event, self.data.base_date_name) - or getattr(event.event, self.data.base_date_name) - or event.date_from - ) - else: - base_date = getattr(event, self.data.base_date_name) or event.date_from - - if self.data.is_after: - new_date = base_date.astimezone(tz) + datetime.timedelta(days=self.data.days) - else: - new_date = base_date.astimezone(tz) - datetime.timedelta(days=self.data.days) - return new_date.date() - - def datetime(self, event) -> datetime.datetime: - from .models import SubEvent + def datetime(self, base: "Event | Order | SubEvent") -> datetime.datetime: + """ + If the RelativeDateWrapper wraps a RelativeDate object: + Resolves the effective datetime of this object in relation to the provided reference. + If the RelativeDateWrapper wraps an absolute date or datetime: + Returns the wrapped absolute datetime. + :param base: the reference which should be used to resolve the datetime in case it is a relative datetime. + :return: datetime.datetime + """ if isinstance(self.data, (datetime.datetime, datetime.date)): return self.data else: - tz = ZoneInfo(event.settings.timezone) - if isinstance(event, SubEvent): - base_date = ( - getattr(event, self.data.base_date_name) - or getattr(event.event, self.data.base_date_name) - or event.date_from - ) - else: - base_date = getattr(event, self.data.base_date_name) or event.date_from - - if self.data.minutes is not None: - if self.data.is_after: - return base_date.astimezone(tz) + datetime.timedelta(minutes=self.data.minutes) - else: - return base_date.astimezone(tz) - datetime.timedelta(minutes=self.data.minutes) - else: - if self.data.is_after: - new_date = (base_date.astimezone(tz) + datetime.timedelta(days=self.data.days)).astimezone(tz) - else: - new_date = (base_date.astimezone(tz) - datetime.timedelta(days=self.data.days)).astimezone(tz) - if self.data.time: - new_date = new_date.replace( - hour=self.data.time.hour, - minute=self.data.time.minute, - second=self.data.time.second - ) - new_date = new_date.astimezone(tz) - return new_date + return self.data.datetime(base) def to_string(self) -> str: if isinstance(self.data, (datetime.datetime, datetime.date)): return self.data.isoformat() else: - if self.data.minutes is not None: - return 'RELDATE/minutes/{}/{}/{}'.format( # - self.data.minutes, - self.data.base_date_name, - 'after' if self.data.is_after else '', - ) - return 'RELDATE/{}/{}/{}/{}'.format( # - self.data.days, - self.data.time.strftime('%H:%M:%S') if self.data.time else '-', - self.data.base_date_name, - 'after' if self.data.is_after else '', - ) + return self.data.to_string() @classmethod def from_string(cls, input: str): if input.startswith('RELDATE/'): - parts = input.split('/') - if parts[1] == 'minutes': - data = RelativeDate( - days=0, - minutes=int(parts[2]), - base_date_name=parts[3], - time=None, - is_after=len(parts) > 4 and parts[4] == "after", - ) - else: - if parts[2] == '-': - time = None - else: - timeparts = parts[2].split(':') - time = datetime.time(hour=int(timeparts[0]), minute=int(timeparts[1]), second=int(timeparts[2])) - try: - data = RelativeDate( - days=int(parts[1] or 0), - base_date_name=parts[3], - time=time, - minutes=None, - is_after=len(parts) > 4 and parts[4] == "after", - ) - except ValueError: - data = RelativeDate( - days=0, - base_date_name=parts[3], - time=time, - minutes=None, - is_after=len(parts) > 4 and parts[4] == "after", - ) - if data.base_date_name not in [k[0] for k in BASE_CHOICES]: - raise ValueError('{} is not a valid base date'.format(data.base_date_name)) + data = RelativeDate.from_string(input) else: data = parser.parse(input) return RelativeDateWrapper(data) @@ -187,7 +325,6 @@ BEFORE_AFTER_CHOICE = ( ('after', _('after')), ) - reldatetimeparts = namedtuple('reldatetimeparts', ( "status", # 0 "absolute", # 1 @@ -202,6 +339,14 @@ reldatetimeparts = namedtuple('reldatetimeparts', ( reldatetimeparts.indizes = reldatetimeparts(*range(9)) +def _get_choices(base_choices: List[BaseChoice]) -> List[Tuple[str, Promise]]: + return [(c.key, c.text) for c in base_choices] + + +def _get_choice_validation_obj(choices: List[BaseChoice]): + return {c.key: {"data-supports-before": c.supports_before, "data-supports-after": c.supports_after} for c in choices} + + class RelativeDateTimeWidget(forms.MultiWidget): template_name = 'pretixbase/forms/widgets/reldatetime.html' parts = reldatetimeparts @@ -209,6 +354,7 @@ class RelativeDateTimeWidget(forms.MultiWidget): def __init__(self, *args, **kwargs): self.status_choices = kwargs.pop('status_choices') base_choices = kwargs.pop('base_choices') + choices = _get_choices(base_choices) def placeholder_datetime_format(): df = get_format('DATETIME_INPUT_FORMATS')[0] @@ -220,21 +366,29 @@ class RelativeDateTimeWidget(forms.MultiWidget): tf = get_format('TIME_INPUT_FORMATS')[0] return datetime.time(8, 30, 0).strftime(tf) - widgets = reldatetimeparts( + widgets = cast(dict[str, Widget | type[Widget]], cast(object, reldatetimeparts( status=forms.RadioSelect(choices=self.status_choices), absolute=forms.DateTimeInput( attrs={'placeholder': lazy(placeholder_datetime_format, str), 'class': 'datetimepicker'} ), rel_days_number=forms.NumberInput(), - rel_mins_relationto=forms.Select(choices=base_choices), + rel_mins_relationto=OptionAttrsSelect( + attrs={'data-relative-choice': True}, + choices=choices, + option_attrs=_get_choice_validation_obj(base_choices) + ), rel_days_timeofday=forms.TimeInput( attrs={'placeholder': lazy(placeholder_time_format, str), 'class': 'timepickerfield'} ), rel_mins_number=forms.NumberInput(), - rel_days_relationto=forms.Select(choices=base_choices), - rel_mins_relation=forms.Select(choices=BEFORE_AFTER_CHOICE), - rel_days_relation=forms.Select(choices=BEFORE_AFTER_CHOICE), - ) + rel_days_relationto=OptionAttrsSelect( + attrs={'data-relative-choice': True}, + choices=choices, + option_attrs=_get_choice_validation_obj(base_choices) + ), + rel_mins_relation=forms.Select(attrs={'data-relation-choice': True}, choices=BEFORE_AFTER_CHOICE), + rel_days_relation=forms.Select(attrs={'data-relation-choice': True}, choices=BEFORE_AFTER_CHOICE), + ))) super().__init__(widgets=widgets, *args, **kwargs) def decompress(self, value): @@ -247,10 +401,10 @@ class RelativeDateTimeWidget(forms.MultiWidget): status="unset", absolute=None, rel_days_number=1, - rel_mins_relationto="date_from", + rel_mins_relationto="event__date_from", rel_days_timeofday=None, rel_mins_number=0, - rel_days_relationto="date_from", + rel_days_relationto="event__date_from", rel_mins_relation="before", rel_days_relation="before" ) @@ -259,10 +413,10 @@ class RelativeDateTimeWidget(forms.MultiWidget): status="absolute", absolute=value.data, rel_days_number=1, - rel_mins_relationto="date_from", + rel_mins_relationto="event__date_from", rel_days_timeofday=None, rel_mins_number=0, - rel_days_relationto="date_from", + rel_days_relationto="event__date_from", rel_mins_relation="before", rel_days_relation="before" ) @@ -271,10 +425,10 @@ class RelativeDateTimeWidget(forms.MultiWidget): status="relative_minutes", absolute=None, rel_days_number=None, - rel_mins_relationto=value.data.base_date_name, + rel_mins_relationto=value.data.key, rel_days_timeofday=None, rel_mins_number=value.data.minutes, - rel_days_relationto=value.data.base_date_name, + rel_days_relationto=value.data.key, rel_mins_relation="after" if value.data.is_after else "before", rel_days_relation="after" if value.data.is_after else "before" ) @@ -282,10 +436,10 @@ class RelativeDateTimeWidget(forms.MultiWidget): status="relative", absolute=None, rel_days_number=value.data.days, - rel_mins_relationto=value.data.base_date_name, + rel_mins_relationto=value.data.key, rel_days_timeofday=value.data.time, rel_mins_number=0, - rel_days_relationto=value.data.base_date_name, + rel_days_relationto=value.data.key, rel_mins_relation="after" if value.data.is_after else "before", rel_days_relation="after" if value.data.is_after else "before" ) @@ -309,17 +463,41 @@ class RelativeDateTimeField(forms.MultiValueField): ('relative', _('Relative date:')), ('relative_minutes', _('Relative time:')), ] + self.relative_to_order = kwargs.pop('relative_to_order', False) + + possible_choices = copy.deepcopy(EVENT_BASE_CHOICES) + if self.relative_to_order: + possible_choices.extend(ORDER_BASE_CHOICES) + if kwargs.get('limit_choices'): limit = kwargs.pop('limit_choices') - choices = [(k, v) for k, v in BASE_CHOICES if k in limit] - else: - choices = BASE_CHOICES + if any(["__" not in l for l in limit]): + _warn_skips = (str(os.path.dirname(__file__)),) + + warnings.warn( + message="Please prefix limit_choices with the base the attributes refer to, for example event__date_from", + category=DeprecationWarning, + skip_file_prefixes=_warn_skips + ) + + possible_choices = [ + c for c in possible_choices if + # new base case as we want limit_choices to be expressed as base__attribute + (c.key in limit) or + # fallback for old event based entries + # if the base is an event, then using only attribute is fine + (c.base == "event" and c.attribute in limit) + ] + if not kwargs.get('required', True): status_choices.insert(0, ('unset', _('Not set'))) + + choices = _get_choices(possible_choices) + fields = reldatetimeparts( status=forms.ChoiceField( choices=status_choices, - required=True + required=True, ), absolute=forms.DateTimeField( required=False @@ -329,7 +507,7 @@ class RelativeDateTimeField(forms.MultiValueField): ), rel_mins_relationto=forms.ChoiceField( choices=choices, - required=False + required=False, ), rel_days_timeofday=forms.TimeField( required=False, @@ -339,7 +517,7 @@ class RelativeDateTimeField(forms.MultiValueField): ), rel_days_relationto=forms.ChoiceField( choices=choices, - required=False + required=False, ), rel_mins_relation=forms.ChoiceField( choices=BEFORE_AFTER_CHOICE, @@ -350,8 +528,9 @@ class RelativeDateTimeField(forms.MultiValueField): required=False ), ) + if 'widget' not in kwargs: - kwargs['widget'] = RelativeDateTimeWidget(status_choices=status_choices, base_choices=choices) + kwargs['widget'] = RelativeDateTimeWidget(status_choices=status_choices, base_choices=possible_choices) kwargs.pop('max_length', 0) kwargs.pop('empty_value', 0) super().__init__( @@ -359,21 +538,24 @@ class RelativeDateTimeField(forms.MultiValueField): ) def set_event(self, event): - self.widget.widgets[reldatetimeparts.indizes.rel_days_relationto].choices = [ - (k, v) for k, v in BASE_CHOICES if getattr(event, k, None) - ] - self.widget.widgets[reldatetimeparts.indizes.rel_mins_relationto].choices = [ - (k, v) for k, v in BASE_CHOICES if getattr(event, k, None) - ] + possible_choices = copy.deepcopy(EVENT_BASE_CHOICES) + if self.relative_to_order: + possible_choices.extend(ORDER_BASE_CHOICES) + + possible_choices = possible_choices + choices = _get_choices(possible_choices) + + self.widget.widgets[reldatetimeparts.indizes.rel_days_relationto].choices = choices + self.widget.widgets[reldatetimeparts.indizes.rel_mins_relationto].choices = choices def compress(self, data_list): if not data_list: return None data = reldatetimeparts(*data_list) - if data.status == 'absolute': - return RelativeDateWrapper(data.absolute) - elif data.status == 'unset': + if data.status == 'unset': return None + elif data.status == 'absolute': + return RelativeDateWrapper(data.absolute) elif data.status == 'relative_minutes': return RelativeDateWrapper(RelativeDate( days=0, @@ -404,6 +586,18 @@ class RelativeDateTimeField(forms.MultiValueField): raise ValidationError(self.error_messages['incomplete']) elif data.status == 'relative_minutes' and (data.rel_mins_number is None or not data.rel_mins_relationto): raise ValidationError(self.error_messages['incomplete']) + elif data.status == 'relative': + choice = BaseChoice.find(BASE_CHOICES, data.rel_days_relationto) + if data.rel_days_relation == "before" and not choice.supports_before: + raise ValidationError(_('A relative date cannot be expressed as "before" for "{}"'.format(choice.text))) + elif data.status == 'relative' and data.rel_days_relation == "after" and not choice.supports_after: + raise ValidationError(_('A relative date cannot be expressed as "after" for "{}"'.format(choice.text))) + elif data.status == 'relative_minutes': + choice = BaseChoice.find(BASE_CHOICES, data.rel_days_relationto) + if data.rel_days_relation == "before" and not choice.supports_before: + raise ValidationError(_('A relative time cannot be expressed as "before" for "{}"'.format(choice.text))) + elif data.rel_days_relation == "after" and not choice.supports_after: + raise ValidationError(_('A relative time cannot be expressed as "after" for "{}"'.format(choice.text))) return super().clean(value) @@ -424,15 +618,20 @@ class RelativeDateWidget(RelativeDateTimeWidget): def __init__(self, *args, **kwargs): self.status_choices = kwargs.pop('status_choices') - widgets = reldateparts( + self.base_choices = kwargs.pop('base_choices') + widgets = cast(dict[str, Widget | type[Widget]], cast(object, reldateparts( status=forms.RadioSelect(choices=self.status_choices), absolute=forms.DateInput( attrs={'class': 'datepickerfield'} ), rel_days_number=forms.NumberInput(), - rel_days_relationto=forms.Select(choices=kwargs.pop('base_choices')), - rel_days_relation=forms.Select(choices=BEFORE_AFTER_CHOICE), - ) + rel_days_relationto=OptionAttrsSelect( + choices=self.base_choices, + option_attrs=_get_choice_validation_obj(self.base_choices), + attrs={'data-relative-choice': True}, + ), + rel_days_relation=forms.Select(choices=BEFORE_AFTER_CHOICE, attrs={'data-relation-choice': True},), + ))) forms.MultiWidget.__init__(self, widgets=widgets, *args, **kwargs) def decompress(self, value): @@ -460,7 +659,7 @@ class RelativeDateWidget(RelativeDateTimeWidget): status="relative", absolute=None, rel_days_number=value.data.days, - rel_days_relationto=value.data.base_date_name, + rel_days_relationto=value.data.key, rel_days_relation="after" if value.data.is_after else "before" ) @@ -474,6 +673,15 @@ class RelativeDateField(RelativeDateTimeField): ] if not kwargs.get('required', True): status_choices.insert(0, ('unset', _('Not set'))) + + self.relative_to_order = kwargs.pop('relative_to_order', False) + + possible_choices = copy.deepcopy(EVENT_BASE_CHOICES) + if self.relative_to_order: + possible_choices.extend(ORDER_BASE_CHOICES) + + choices = _get_choices(possible_choices) + fields = reldateparts( status=forms.ChoiceField( choices=status_choices, @@ -486,33 +694,37 @@ class RelativeDateField(RelativeDateTimeField): required=False ), rel_days_relationto=forms.ChoiceField( - choices=BASE_CHOICES, - required=False + choices=choices, + required=False, ), rel_days_relation=forms.ChoiceField( choices=BEFORE_AFTER_CHOICE, required=False ), ) + if 'widget' not in kwargs: - kwargs['widget'] = RelativeDateWidget(status_choices=status_choices, base_choices=BASE_CHOICES) + kwargs['widget'] = RelativeDateWidget(status_choices=status_choices, base_choices=possible_choices) forms.MultiValueField.__init__( self, fields=fields, require_all_fields=False, *args, **kwargs ) def set_event(self, event): - self.widget.widgets[reldateparts.indizes.rel_days_relationto].choices = [ - (k, v) for k, v in BASE_CHOICES if getattr(event, k, None) + choices = [ + (c.key, c.text) for c in EVENT_BASE_CHOICES if getattr(event, c.attribute, None) ] + if self.relative_to_order: + choices += [(c.key, c.text) for c in ORDER_BASE_CHOICES] + self.widget.widgets[reldateparts.indizes.rel_days_relationto].choices = choices def compress(self, data_list): if not data_list: return None data = reldateparts(*data_list) - if data.status == 'absolute': - return RelativeDateWrapper(data.absolute) - elif data.status == 'unset': + if data.status == 'unset': return None + elif data.status == 'absolute': + return RelativeDateWrapper(data.absolute) else: return RelativeDateWrapper(RelativeDate( days=data.rel_days_number, @@ -525,8 +737,14 @@ class RelativeDateField(RelativeDateTimeField): data = reldateparts(*value) if data.status == 'absolute' and not data.absolute: raise ValidationError(self.error_messages['incomplete']) - elif data.status == 'relative' and (data.rel_days_number is None or not data.rel_days_relationto): - raise ValidationError(self.error_messages['incomplete']) + if data.status == 'relative': + choice = BaseChoice.find(BASE_CHOICES, data.rel_days_relationto) + if data.rel_days_number is None or not data.rel_days_relationto: + raise ValidationError(self.error_messages['incomplete']) + elif data.rel_days_relation == "before" and not choice.supports_before: + raise ValidationError(_("A relative date cannot be expressed as 'before' for '{}'".format(choice.text))) + elif data.rel_days_relation == "after" and not choice.supports_after: + raise ValidationError(_("A relative date cannot be expressed as 'after' for '{}'".format(choice.text))) return forms.MultiValueField.clean(self, value) diff --git a/src/pretix/control/forms/subevents.py b/src/pretix/control/forms/subevents.py index b8e004c33..21e232063 100644 --- a/src/pretix/control/forms/subevents.py +++ b/src/pretix/control/forms/subevents.py @@ -94,14 +94,14 @@ class SubEventBulkForm(SubEventForm): label=_('Start of presale'), help_text=_('Optional. No products will be sold before this date.'), required=False, - limit_choices=('date_from', 'date_to'), + limit_choices=('event__date_from', 'event__date_to'), ) rel_presale_end = RelativeDateTimeField( label=_('End of presale'), help_text=_('Optional. No products will be sold after this date. If you do not set this value, the presale ' 'will end after the end date of your event.'), required=False, - limit_choices=('date_from', 'date_to'), + limit_choices=('event__date_from', 'event__date_to'), ) skip_if_overlap = forms.BooleanField( label=pgettext_lazy('subevent', 'Skip dates that overlap with any existing date'), @@ -333,12 +333,12 @@ class BulkSubEventItemForm(SubEventItemForm): rel_available_from = RelativeDateTimeField( label=_('Available from'), required=False, - limit_choices=('date_from', 'date_to'), + limit_choices=('event__date_from', 'event__date_to'), ) rel_available_until = RelativeDateTimeField( label=_('Available until'), required=False, - limit_choices=('date_from', 'date_to'), + limit_choices=('event__date_from', 'event__date_to'), ) def __init__(self, *args, **kwargs): @@ -355,12 +355,12 @@ class BulkSubEventItemVariationForm(SubEventItemVariationForm): rel_available_from = RelativeDateTimeField( label=_('Available from'), required=False, - limit_choices=('date_from', 'date_to'), + limit_choices=('event__date_from', 'event__date_to'), ) rel_available_until = RelativeDateTimeField( label=_('Available_until'), required=False, - limit_choices=('date_from', 'date_to'), + limit_choices=('event__date_from', 'event__date_to'), ) def __init__(self, *args, **kwargs): diff --git a/src/pretix/control/templates/pretixcontrol/base.html b/src/pretix/control/templates/pretixcontrol/base.html index bf9181d22..e6b544be1 100644 --- a/src/pretix/control/templates/pretixcontrol/base.html +++ b/src/pretix/control/templates/pretixcontrol/base.html @@ -58,6 +58,7 @@ + diff --git a/src/pretix/control/views/subevents.py b/src/pretix/control/views/subevents.py index b40a41542..5cbf7fd31 100644 --- a/src/pretix/control/views/subevents.py +++ b/src/pretix/control/views/subevents.py @@ -431,7 +431,7 @@ class SubEventEditorMixin(MetaDataEditorMixin): days = (self.copy_from.date_from.astimezone(tz).date() - value.astimezone(tz).date()).days return RelativeDateWrapper(RelativeDate( days=abs(days), - base_date_name='date_from', + base_date_name='event__date_from', time=value.astimezone(tz).time(), minutes=None, is_after=days < 0, diff --git a/src/pretix/static/pretixbase/js/reldate.js b/src/pretix/static/pretixbase/js/reldate.js new file mode 100644 index 000000000..bc7defdfb --- /dev/null +++ b/src/pretix/static/pretixbase/js/reldate.js @@ -0,0 +1,42 @@ +$(document).on('pretix:bind-forms', () => { + document.querySelectorAll('.reldatetime, .reldate').forEach(container => { + const groups = container.querySelectorAll('.radio') + + groups.forEach(group => { + const referenceSelect = group.querySelector('select[data-relative-choice]') + const beforeAfterSelect = group.querySelector('select[data-relation-choice]') + if (!referenceSelect || !beforeAfterSelect) return + + const beforeOption = beforeAfterSelect.querySelector('option[value="before"]') + const afterOption = beforeAfterSelect.querySelector('option[value="after"]') + + const updateBeforeOption = () => { + let supportsBefore = referenceSelect.selectedOptions[0].hasAttribute('data-supports-before') + if (beforeOption) { + beforeOption.disabled = !beforeOption.disabled && !supportsBefore + } + + let supportsAfter = referenceSelect.selectedOptions[0].hasAttribute('data-supports-after') + if (afterOption) { + afterOption.disabled = !afterOption.disabled && !supportsAfter + } + + let dirty = false + if (beforeOption.disabled && beforeAfterSelect.value === 'before') { + beforeAfterSelect.value = 'after' + dirty = true + } + if (afterOption.disabled && beforeAfterSelect.value === 'after') { + beforeAfterSelect.value = 'before' + dirty = true + } + + if (dirty) { + beforeAfterSelect.dispatchEvent(new Event('change', {bubbles: true})) + } + } + referenceSelect.addEventListener('change', updateBeforeOption) + updateBeforeOption() + }) + }) +}) diff --git a/src/tests/base/test_models.py b/src/tests/base/test_models.py index 6f2359db1..41d9d67ab 100644 --- a/src/tests/base/test_models.py +++ b/src/tests/base/test_models.py @@ -473,7 +473,7 @@ class QuotaTestCase(BaseQuotaTestCase): @classscope(attr='o') def test_waitinglist_auto_disable(self): self.event.settings.waiting_list_auto_disable = RelativeDateWrapper( - RelativeDate(days=0, time=None, base_date_name='date_from', minutes=20, is_after=True) + RelativeDate(days=0, time=None, base_date_name='event__date_from', minutes=20, is_after=True) ) self.quota.items.add(self.item1) self.quota.size = 1 @@ -484,7 +484,7 @@ class QuotaTestCase(BaseQuotaTestCase): self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_ORDERED, 0)) self.assertEqual(self.item1.check_quotas(count_waitinglist=False), (Quota.AVAILABILITY_OK, 1)) self.event.settings.waiting_list_auto_disable = RelativeDateWrapper( - RelativeDate(days=0, time=None, base_date_name='date_from', minutes=20, is_after=False) + RelativeDate(days=0, time=None, base_date_name='event__date_from', minutes=20, is_after=False) ) self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1)) self.assertEqual(self.item1.check_quotas(count_waitinglist=False), (Quota.AVAILABILITY_OK, 1)) @@ -1192,7 +1192,7 @@ class OrderTestCase(BaseQuotaTestCase): self.op2.subevent = se2 self.op2.save() self.event.settings.set('payment_term_last', RelativeDateWrapper( - RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None) )) self.order.status = Order.STATUS_EXPIRED @@ -1347,7 +1347,7 @@ class OrderTestCase(BaseQuotaTestCase): self.op2.subevent = se2 self.op2.save() self.event.settings.set('last_order_modification_date', RelativeDateWrapper( - RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None) )) assert self.order.can_modify_answers self.op2.subevent = se3 @@ -1363,7 +1363,7 @@ class OrderTestCase(BaseQuotaTestCase): self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=datetime.timezone.utc) self.event.save() self.event.settings.set('payment_term_last', RelativeDateWrapper( - RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None) )) assert self.order.payment_term_last == datetime.datetime(2017, 5, 1, 23, 59, 59, tzinfo=datetime.timezone.utc) @@ -1379,7 +1379,7 @@ class OrderTestCase(BaseQuotaTestCase): self.op2.subevent = se2 self.op2.save() self.event.settings.set('payment_term_last', RelativeDateWrapper( - RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None) )) assert self.order.payment_term_last > now() self.op2.subevent = se3 @@ -1395,7 +1395,7 @@ class OrderTestCase(BaseQuotaTestCase): self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=datetime.timezone.utc) self.event.save() self.event.settings.set('ticket_download_date', RelativeDateWrapper( - RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None) )) assert self.order.ticket_download_date == datetime.datetime(2017, 5, 1, 12, 0, 0, tzinfo=datetime.timezone.utc) @@ -1411,7 +1411,7 @@ class OrderTestCase(BaseQuotaTestCase): self.op2.subevent = se2 self.op2.save() self.event.settings.set('ticket_download_date', RelativeDateWrapper( - RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None) )) assert self.order.ticket_download_date > now() self.op2.subevent = se3 @@ -1582,13 +1582,13 @@ class OrderTestCase(BaseQuotaTestCase): assert self.order.user_cancel_deadline is None self.event.settings.set('cancel_allow_user_until', RelativeDateWrapper( - RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None) )) self.order = Order.objects.get(pk=self.order.pk) assert self.order.user_cancel_deadline > now() assert self.order.user_cancel_allowed self.event.settings.set('cancel_allow_user_until', RelativeDateWrapper( - RelativeDate(days=4, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None) + RelativeDate(days=4, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None) )) self.order = Order.objects.get(pk=self.order.pk) assert self.order.user_cancel_deadline < now() @@ -1607,7 +1607,7 @@ class OrderTestCase(BaseQuotaTestCase): self.op2.save() self.event.settings.set('cancel_allow_user_until', RelativeDateWrapper( - RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None) )) self.order = Order.objects.get(pk=self.order.pk) assert self.order.user_cancel_deadline < now() @@ -2018,13 +2018,13 @@ class OrderTestCase(BaseQuotaTestCase): assert self.order.user_change_deadline is None self.event.settings.set('change_allow_user_until', RelativeDateWrapper( - RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None) )) self.order = Order.objects.get(pk=self.order.pk) assert self.order.user_change_deadline > now() assert self.order.user_change_allowed self.event.settings.set('change_allow_user_until', RelativeDateWrapper( - RelativeDate(days=4, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None) + RelativeDate(days=4, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None) )) self.order = Order.objects.get(pk=self.order.pk) assert self.order.user_change_deadline < now() @@ -2046,7 +2046,7 @@ class OrderTestCase(BaseQuotaTestCase): self.op2.save() self.event.settings.set('change_allow_user_until', RelativeDateWrapper( - RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None) )) self.order = Order.objects.get(pk=self.order.pk) assert self.order.user_change_deadline < now() diff --git a/src/tests/base/test_orders.py b/src/tests/base/test_orders.py index 821a4fc5f..907a76dbc 100644 --- a/src/tests/base/test_orders.py +++ b/src/tests/base/test_orders.py @@ -204,7 +204,7 @@ def test_expiry_last_relative(event): event.date_from = now() + timedelta(days=5) event.save() event.settings.set('payment_term_last', RelativeDateWrapper( - RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None) )) order = _create_order(event, email='dummy@example.org', positions=[], now_dt=today, @@ -245,7 +245,7 @@ def test_expiry_last_relative_subevents(event): ) event.settings.set('payment_term_last', RelativeDateWrapper( - RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None) )) order = _create_order(event, email='dummy@example.org', positions=[cp1, cp2], now_dt=today, diff --git a/src/tests/base/test_payment.py b/src/tests/base/test_payment.py index 9b88e37a7..a2823b915 100644 --- a/src/tests/base/test_payment.py +++ b/src/tests/base/test_payment.py @@ -133,7 +133,7 @@ def test_availability_date_relative(event): event.save() prov = DummyPaymentProvider(event) prov.settings.set('_availability_date', RelativeDateWrapper( - RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None) )) utc = datetime.timezone.utc @@ -150,7 +150,7 @@ def test_availability_start_relative(event): event.save() prov = DummyPaymentProvider(event) prov.settings.set('_availability_start', RelativeDateWrapper( - RelativeDate(days=2, time=datetime.time(12, 0), base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=datetime.time(12, 0), base_date_name='event__date_from', minutes=None) )) utc = datetime.timezone.utc @@ -193,12 +193,12 @@ def test_availability_date_cart_relative_subevents(event): prov = DummyPaymentProvider(event) prov.settings.set('_availability_date', RelativeDateWrapper( - RelativeDate(days=3, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=3, time=None, base_date_name='event__date_from', minutes=None) )) assert prov._is_available_by_time(cart_id="123") prov.settings.set('_availability_date', RelativeDateWrapper( - RelativeDate(days=4, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=4, time=None, base_date_name='event__date_from', minutes=None) )) assert not prov._is_available_by_time(cart_id="123") @@ -233,11 +233,11 @@ def test_availability_date_order_relative_subevents(event): prov = DummyPaymentProvider(event) prov.settings.set('_availability_date', RelativeDateWrapper( - RelativeDate(days=3, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=3, time=None, base_date_name='event__date_from', minutes=None) )) assert prov._is_available_by_time(order=order) prov.settings.set('_availability_date', RelativeDateWrapper( - RelativeDate(days=4, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=4, time=None, base_date_name='event__date_from', minutes=None) )) assert not prov._is_available_by_time(order=order) diff --git a/src/tests/base/test_reldate.py b/src/tests/base/test_reldate.py index d5f0db9ec..92cefb1a5 100644 --- a/src/tests/base/test_reldate.py +++ b/src/tests/base/test_reldate.py @@ -19,13 +19,13 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from datetime import datetime, time +from datetime import datetime, time, timedelta from zoneinfo import ZoneInfo import pytest from django_scopes import scope -from pretix.base.models import Event, Organizer +from pretix.base.models import Event, Order, Organizer from pretix.base.reldate import RelativeDate, RelativeDateWrapper TOKYO = ZoneInfo('Asia/Tokyo') @@ -40,7 +40,6 @@ def event(): date_from=datetime(2017, 12, 27, 5, 0, 0, tzinfo=TOKYO), presale_start=datetime(2017, 12, 1, 5, 0, 0, tzinfo=TOKYO), plugins='pretix.plugins.banktransfer' - ) event.settings.timezone = "Asia/Tokyo" return event @@ -56,57 +55,57 @@ def test_absolute_date(event): @pytest.mark.django_db def test_relative_date_without_time(event): - rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='date_from', minutes=None)) + rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__date_from', minutes=None)) assert rdw.datetime(event).astimezone(TOKYO) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO) - assert rdw.to_string() == 'RELDATE/1/-/date_from/' - rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='date_from', minutes=None, is_after=True)) + assert rdw.to_string() == 'RELDATE/1/-/event__date_from/' + rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__date_from', minutes=None, is_after=True)) assert rdw.datetime(event).astimezone(TOKYO) == datetime(2017, 12, 28, 5, 0, 0, tzinfo=TOKYO) - assert rdw.to_string() == 'RELDATE/1/-/date_from/after' + assert rdw.to_string() == 'RELDATE/1/-/event__date_from/after' @pytest.mark.django_db def test_relative_date_other_base_point(event): with scope(organizer=event.organizer): - rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='presale_start', minutes=None)) + rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__presale_start', minutes=None)) assert rdw.datetime(event) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO) - assert rdw.to_string() == 'RELDATE/1/-/presale_start/' + assert rdw.to_string() == 'RELDATE/1/-/event__presale_start/' # presale_end is unset, defaults to date_from - rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='presale_end', minutes=None)) + rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__presale_end', minutes=None)) assert rdw.datetime(event) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO) - assert rdw.to_string() == 'RELDATE/1/-/presale_end/' + assert rdw.to_string() == 'RELDATE/1/-/event__presale_end/' # subevent base se = event.subevents.create(name="SE1", date_from=datetime(2017, 11, 27, 5, 0, 0, tzinfo=TOKYO)) - rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='date_from', minutes=None)) + rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__date_from', minutes=None)) assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO) # presale_start is unset on subevent, default to event - rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='presale_start', minutes=None)) + rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__presale_start', minutes=None)) assert rdw.datetime(se) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO) # presale_end is unset on all, default to date_from of subevent - rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='presale_end', minutes=None)) + rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__presale_end', minutes=None)) assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO) @pytest.mark.django_db def test_relative_date_in_minutes(event): - rdw = RelativeDateWrapper(RelativeDate(days=0, time=None, base_date_name='date_from', minutes=60)) - assert rdw.to_string() == 'RELDATE/minutes/60/date_from/' + rdw = RelativeDateWrapper(RelativeDate(days=0, time=None, base_date_name='event__date_from', minutes=60)) + assert rdw.to_string() == 'RELDATE/minutes/60/event__date_from/' assert rdw.datetime(event) == datetime(2017, 12, 27, 4, 0, 0, tzinfo=TOKYO) - rdw = RelativeDateWrapper(RelativeDate(days=0, time=None, base_date_name='date_from', minutes=60, is_after=True)) - assert rdw.to_string() == 'RELDATE/minutes/60/date_from/after' + rdw = RelativeDateWrapper(RelativeDate(days=0, time=None, base_date_name='event__date_from', minutes=60, is_after=True)) + assert rdw.to_string() == 'RELDATE/minutes/60/event__date_from/after' assert rdw.datetime(event) == datetime(2017, 12, 27, 6, 0, 0, tzinfo=TOKYO) @pytest.mark.django_db def test_relative_date_with_time(event): - rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(8, 5, 13), base_date_name='date_from', minutes=None)) - assert rdw.to_string() == 'RELDATE/1/08:05:13/date_from/' + rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(8, 5, 13), base_date_name='event__date_from', minutes=None)) + assert rdw.to_string() == 'RELDATE/1/08:05:13/event__date_from/' assert rdw.datetime(event) == datetime(2017, 12, 26, 8, 5, 13, tzinfo=TOKYO) - rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(8, 5, 13), base_date_name='date_from', minutes=None, is_after=True)) - assert rdw.to_string() == 'RELDATE/1/08:05:13/date_from/after' + rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(8, 5, 13), base_date_name='event__date_from', minutes=None, is_after=True)) + assert rdw.to_string() == 'RELDATE/1/08:05:13/event__date_from/after' assert rdw.datetime(event) == datetime(2017, 12, 28, 8, 5, 13, tzinfo=TOKYO) @@ -115,35 +114,94 @@ def test_relative_date_with_time_around_dst(event): event.settings.timezone = "Europe/Berlin" event.date_from = datetime(2020, 3, 29, 18, 0, 0, tzinfo=BERLIN) - rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(18, 0, 0), base_date_name='date_from', minutes=None)) - assert rdw.to_string() == 'RELDATE/1/18:00:00/date_from/' + rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(18, 0, 0), base_date_name='event__date_from', minutes=None)) + assert rdw.to_string() == 'RELDATE/1/18:00:00/event__date_from/' assert rdw.datetime(event) == datetime(2020, 3, 28, 18, 0, 0, tzinfo=BERLIN) - rdw = RelativeDateWrapper(RelativeDate(days=0, time=time(2, 30, 0), base_date_name='date_from', minutes=None)) - assert rdw.to_string() == 'RELDATE/0/02:30:00/date_from/' + rdw = RelativeDateWrapper(RelativeDate(days=0, time=time(2, 30, 0), base_date_name='event__date_from', minutes=None)) + assert rdw.to_string() == 'RELDATE/0/02:30:00/event__date_from/' assert rdw.datetime(event) == datetime(2020, 3, 29, 2, 30, 0, tzinfo=BERLIN) event.date_from = datetime(2020, 10, 25, 18, 0, 0, tzinfo=BERLIN) - rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(18, 0, 0), base_date_name='date_from', minutes=None)) - assert rdw.to_string() == 'RELDATE/1/18:00:00/date_from/' + rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(18, 0, 0), base_date_name='event__date_from', minutes=None)) + assert rdw.to_string() == 'RELDATE/1/18:00:00/event__date_from/' assert rdw.datetime(event) == datetime(2020, 10, 24, 18, 0, 0, tzinfo=BERLIN) - rdw = RelativeDateWrapper(RelativeDate(days=0, time=time(2, 30, 0), base_date_name='date_from', minutes=None)) - assert rdw.to_string() == 'RELDATE/0/02:30:00/date_from/' + rdw = RelativeDateWrapper(RelativeDate(days=0, time=time(2, 30, 0), base_date_name='event__date_from', minutes=None)) + assert rdw.to_string() == 'RELDATE/0/02:30:00/event__date_from/' assert rdw.datetime(event) == datetime(2020, 10, 25, 2, 30, 0, tzinfo=BERLIN) -def test_unserialize(): +def test_unserialize_backwards_compatibility(): + d = datetime(2017, 12, 25, 10, 0, 0, tzinfo=TOKYO) + rdw = RelativeDateWrapper.from_string(d.isoformat()) + assert rdw.data == d + + # keeping the test for the old from_string_format to ensure that we don't break anything + rdw = RelativeDateWrapper.from_string('RELDATE/1/-/date_from/') + assert rdw.data == RelativeDate(days=1, time=None, base_date_name='date_from', minutes=None) + + # keeping the test for the old from_string_format to ensure that we don't break anything + rdw = RelativeDateWrapper.from_string('RELDATE/1/18:05:13/date_from/') + assert rdw.data == RelativeDate(days=1, time=time(18, 5, 13), base_date_name='date_from', minutes=None) + + # keeping the test for the old from_string_format to ensure that we don't break anything + rdw = RelativeDateWrapper.from_string('RELDATE/minutes/60/date_from/') + assert rdw.data == RelativeDate(days=0, time=None, base_date_name='date_from', minutes=60) + + +def test_backwards_compatibility(): + # the data model of RelativeDate had to be extended to support other models as relation target + # previously only: + # - date_from + # - date_to + # - date_admission + # - presale_start + # - presale_end + # where valid values for the fourth slot (base_date_names) of the serialized form. + # the relationship in this case always pointed at event + # so any preexisting base_date_names without __ should continue to work and upgrade to event__{old_base_date_name} d = datetime(2017, 12, 25, 10, 0, 0, tzinfo=TOKYO) rdw = RelativeDateWrapper.from_string(d.isoformat()) assert rdw.data == d rdw = RelativeDateWrapper.from_string('RELDATE/1/-/date_from/') - assert rdw.data == RelativeDate(days=1, time=None, base_date_name='date_from', minutes=None) + assert rdw.to_string() == 'RELDATE/1/-/event__date_from/' - rdw = RelativeDateWrapper.from_string('RELDATE/1/18:05:13/date_from/') - assert rdw.data == RelativeDate(days=1, time=time(18, 5, 13), base_date_name='date_from', minutes=None) + rdw = RelativeDateWrapper.from_string('RELDATE/1/-/date_to/') + assert rdw.to_string() == 'RELDATE/1/-/event__date_to/' - rdw = RelativeDateWrapper.from_string('RELDATE/minutes/60/date_from/') - assert rdw.data == RelativeDate(days=0, time=None, base_date_name='date_from', minutes=60) + rdw = RelativeDateWrapper.from_string('RELDATE/1/-/date_admission/') + assert rdw.to_string() == 'RELDATE/1/-/event__date_admission/' + + rdw = RelativeDateWrapper.from_string('RELDATE/1/-/presale_start/') + assert rdw.to_string() == 'RELDATE/1/-/event__presale_start/' + + rdw = RelativeDateWrapper.from_string('RELDATE/1/-/presale_end/') + assert rdw.to_string() == 'RELDATE/1/-/event__presale_end/' + + # new order base_date_names should not work without __ + with pytest.raises(TypeError): + RelativeDateWrapper.from_string('RELDATE/1/-/datetime/') + with pytest.raises(TypeError): + RelativeDateWrapper.from_string('RELDATE/1/-/expires/') + + +@pytest.mark.django_db +def test_relative_to_order(event): + with scope(organizer=event.organizer): + order_moment = datetime(2020, 3, 29, 18, 0, 0, tzinfo=TOKYO) + + order = Order.objects.create( + code='FOO', event=event, email='dummy@dummy.test', + status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1", + datetime=order_moment, + expires=order_moment + timedelta(days=10), + sales_channel=event.organizer.sales_channels.get(identifier="web"), + total=23, locale='en' + ) + + rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='order__datetime', minutes=None, is_after=True)) + assert rdw.datetime(order).astimezone(TOKYO) == datetime(2020, 3, 30, 18, 0, 0, tzinfo=TOKYO) + assert rdw.to_string() == 'RELDATE/1/-/order__datetime/after' diff --git a/src/tests/base/test_waitinglist.py b/src/tests/base/test_waitinglist.py index df90eaa87..ffa155d9e 100644 --- a/src/tests/base/test_waitinglist.py +++ b/src/tests/base/test_waitinglist.py @@ -235,7 +235,7 @@ class WaitingListTestCase(TestCase): self.event.settings.set('waiting_list_enabled', True) self.event.settings.set('waiting_list_auto', True) self.event.settings.waiting_list_auto_disable = RelativeDateWrapper( - RelativeDate(days=0, time=None, base_date_name='date_from', minutes=20, is_after=False) + RelativeDate(days=0, time=None, base_date_name='event__date_from', minutes=20, is_after=False) ) self.event.save() with scope(organizer=self.o): diff --git a/src/tests/control/test_events.py b/src/tests/control/test_events.py index efd8a64d8..e363c4889 100644 --- a/src/tests/control/test_events.py +++ b/src/tests/control/test_events.py @@ -481,7 +481,7 @@ class EventsTest(SoupTest): 'payment_term_last_0': 'absolute', 'payment_term_last_1': (self.event1.presale_end - datetime.timedelta(1)).strftime('%Y-%m-%d'), 'payment_term_last_2': '0', - 'payment_term_last_3': 'date_from', + 'payment_term_last_3': 'event__date_from', 'tax_rule_payment': 'default', }) assert doc.select('.alert-danger') @@ -496,7 +496,7 @@ class EventsTest(SoupTest): 'payment_term_last_0': 'relative', 'payment_term_last_1': '', 'payment_term_last_2': '10', - 'payment_term_last_3': 'date_from', + 'payment_term_last_3': 'event__date_from', 'tax_rule_payment': 'default', }) assert doc.select('.alert-danger') diff --git a/src/tests/control/test_subevents.py b/src/tests/control/test_subevents.py index 1089e9891..532b1fa66 100644 --- a/src/tests/control/test_subevents.py +++ b/src/tests/control/test_subevents.py @@ -230,19 +230,19 @@ class SubEventsTest(SoupTest): 'rel_presale_start_0': 'unset', 'rel_presale_start_1': '', 'rel_presale_start_2': '1', - 'rel_presale_start_3': 'date_from', + 'rel_presale_start_3': 'event__date_from', 'rel_presale_start_4': '', 'rel_presale_start_5': '', - 'rel_presale_start_6': 'date_from', + 'rel_presale_start_6': 'event__date_from', 'rel_presale_start_7': 'before', 'rel_presale_start_8': 'before', 'rel_presale_end_1': '', 'rel_presale_end_0': 'relative', 'rel_presale_end_2': '1', - 'rel_presale_end_3': 'date_from', + 'rel_presale_end_3': 'event__date_from', 'rel_presale_end_4': '13:29:31', 'rel_presale_end_5': '', - 'rel_presale_end_6': 'date_from', + 'rel_presale_end_6': 'event__date_from', 'rel_presale_end_7': 'before', 'rel_presale_end_8': 'before', 'quotas-TOTAL_FORMS': '1', @@ -328,19 +328,19 @@ class SubEventsTest(SoupTest): 'rel_presale_start_0': 'unset', 'rel_presale_start_1': '', 'rel_presale_start_2': '1', - 'rel_presale_start_3': 'date_from', + 'rel_presale_start_3': 'event__date_from', 'rel_presale_start_4': '', 'rel_presale_start_5': '', - 'rel_presale_start_6': 'date_from', + 'rel_presale_start_6': 'event__date_from', 'rel_presale_start_7': 'before', 'rel_presale_start_8': 'before', 'rel_presale_end_1': '', 'rel_presale_end_0': 'relative', 'rel_presale_end_2': '1', - 'rel_presale_end_3': 'date_from', + 'rel_presale_end_3': 'event__date_from', 'rel_presale_end_4': '13:29:31', 'rel_presale_end_5': '', - 'rel_presale_end_6': 'date_from', + 'rel_presale_end_6': 'event__date_from', 'rel_presale_end_7': 'before', 'rel_presale_end_8': 'before', 'quotas-TOTAL_FORMS': '1', @@ -403,19 +403,19 @@ class SubEventsTest(SoupTest): 'rel_presale_start_0': 'unset', 'rel_presale_start_1': '', 'rel_presale_start_2': '1', - 'rel_presale_start_3': 'date_from', + 'rel_presale_start_3': 'event__date_from', 'rel_presale_start_4': '', 'rel_presale_start_5': '', - 'rel_presale_start_6': 'date_from', + 'rel_presale_start_6': 'event__date_from', 'rel_presale_start_7': 'before', 'rel_presale_start_8': 'before', 'rel_presale_end_1': '', 'rel_presale_end_0': 'relative', 'rel_presale_end_2': '1', - 'rel_presale_end_3': 'date_from', + 'rel_presale_end_3': 'event__date_from', 'rel_presale_end_4': '13:29:31', 'rel_presale_end_5': '', - 'rel_presale_end_6': 'date_from', + 'rel_presale_end_6': 'event__date_from', 'rel_presale_end_7': 'before', 'rel_presale_end_8': 'before', 'quotas-TOTAL_FORMS': '1', @@ -479,19 +479,19 @@ class SubEventsTest(SoupTest): 'rel_presale_start_0': 'unset', 'rel_presale_start_1': '', 'rel_presale_start_2': '1', - 'rel_presale_start_3': 'date_from', + 'rel_presale_start_3': 'event__date_from', 'rel_presale_start_4': '', 'rel_presale_start_5': '', - 'rel_presale_start_6': 'date_from', + 'rel_presale_start_6': 'event__date_from', 'rel_presale_start_7': 'before', 'rel_presale_start_8': 'before', 'rel_presale_end_1': '', 'rel_presale_end_0': 'relative', 'rel_presale_end_2': '1', - 'rel_presale_end_3': 'date_from', + 'rel_presale_end_3': 'event__date_from', 'rel_presale_end_4': '02:30:00', 'rel_presale_end_5': '', - 'rel_presale_end_6': 'date_from', + 'rel_presale_end_6': 'event__date_from', 'rel_presale_end_7': 'before', 'rel_presale_end_8': 'before', 'quotas-TOTAL_FORMS': '1', @@ -567,19 +567,19 @@ class SubEventsTest(SoupTest): 'rel_presale_start_0': 'unset', 'rel_presale_start_1': '', 'rel_presale_start_2': '1', - 'rel_presale_start_3': 'date_from', + 'rel_presale_start_3': 'event__date_from', 'rel_presale_start_4': '', 'rel_presale_start_5': '', - 'rel_presale_start_6': 'date_from', + 'rel_presale_start_6': 'event__date_from', 'rel_presale_start_7': 'before', 'rel_presale_start_8': 'before', 'rel_presale_end_1': '', 'rel_presale_end_0': 'relative', 'rel_presale_end_2': '1', - 'rel_presale_end_3': 'date_from', + 'rel_presale_end_3': 'event__date_from', 'rel_presale_end_4': '13:29:31', 'rel_presale_end_5': '', - 'rel_presale_end_6': 'date_from', + 'rel_presale_end_6': 'event__date_from', 'rel_presale_end_7': 'before', 'rel_presale_end_8': 'before', 'quotas-TOTAL_FORMS': '1', @@ -639,19 +639,19 @@ class SubEventsTest(SoupTest): 'rel_presale_start_0': 'unset', 'rel_presale_start_1': '', 'rel_presale_start_2': '1', - 'rel_presale_start_3': 'date_from', + 'rel_presale_start_3': 'event__date_from', 'rel_presale_start_4': '', 'rel_presale_start_5': '', - 'rel_presale_start_6': 'date_from', + 'rel_presale_start_6': 'event__date_from', 'rel_presale_start_7': 'before', 'rel_presale_start_8': 'before', 'rel_presale_end_0': 'unset', 'rel_presale_end_1': '', 'rel_presale_end_2': '1', - 'rel_presale_end_3': 'date_from', + 'rel_presale_end_3': 'event__date_from', 'rel_presale_end_4': '13:29:31', 'rel_presale_end_5': '', - 'rel_presale_end_6': 'date_from', + 'rel_presale_end_6': 'event__date_from', 'rel_presale_end_7': 'before', 'rel_presale_end_8': 'before', 'quotas-TOTAL_FORMS': '1', @@ -711,19 +711,19 @@ class SubEventsTest(SoupTest): 'rel_presale_start_0': 'unset', 'rel_presale_start_1': '', 'rel_presale_start_2': '1', - 'rel_presale_start_3': 'date_from', + 'rel_presale_start_3': 'event__date_from', 'rel_presale_start_4': '', 'rel_presale_start_5': '', - 'rel_presale_start_6': 'date_from', + 'rel_presale_start_6': 'event__date_from', 'rel_presale_start_7': 'before', 'rel_presale_start_8': 'before', 'rel_presale_end_0': 'unset', 'rel_presale_end_1': '', 'rel_presale_end_2': '1', - 'rel_presale_end_3': 'date_from', + 'rel_presale_end_3': 'event__date_from', 'rel_presale_end_4': '13:29:31', 'rel_presale_end_5': '', - 'rel_presale_end_6': 'date_from', + 'rel_presale_end_6': 'event__date_from', 'rel_presale_end_7': 'before', 'rel_presale_end_8': 'before', 'quotas-TOTAL_FORMS': '1', diff --git a/src/tests/presale/test_event.py b/src/tests/presale/test_event.py index 70dade2be..053a5eff4 100644 --- a/src/tests/presale/test_event.py +++ b/src/tests/presale/test_event.py @@ -1132,7 +1132,7 @@ class WaitingListTest(EventTestMixin, SoupTest): def test_auto_disable(self): self.event.settings.set('waiting_list_enabled', True) self.event.settings.waiting_list_auto_disable = RelativeDateWrapper( - RelativeDate(days=900, time=datetime.time(9, 0, 0), base_date_name='date_from', minutes=None, is_after=False) + RelativeDate(days=900, time=datetime.time(9, 0, 0), base_date_name='event__date_from', minutes=None, is_after=False) ) response = self.client.get( '/%s/%s/' % (self.orga.slug, self.event.slug) diff --git a/src/tests/presale/test_order_change.py b/src/tests/presale/test_order_change.py index b53d6ce59..648262578 100644 --- a/src/tests/presale/test_order_change.py +++ b/src/tests/presale/test_order_change.py @@ -1527,7 +1527,7 @@ class OrderChangeAddonsTest(BaseOrdersTest): self.order.save() self._subevent_setup() self.event.settings.set('payment_term_last', RelativeDateWrapper( - RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None) + RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None) )) response = self.client.get( diff --git a/src/tests/presale/test_orders.py b/src/tests/presale/test_orders.py index 9606938b8..72f4523d3 100644 --- a/src/tests/presale/test_orders.py +++ b/src/tests/presale/test_orders.py @@ -1076,7 +1076,7 @@ class OrdersTest(BaseOrdersTest): self.event.date_from = now() + datetime.timedelta(days=3) self.event.save() self.event.settings.set('ticket_download_date', RelativeDateWrapper(RelativeDate( - base_date_name='date_from', days=2, time=None, minutes=None + base_date_name='event__date_from', days=2, time=None, minutes=None ))) response = self.client.post( '/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code, -- 2.54.0 From 4e5fbacf6d13ddb0420a37829d1d1005f38a1fc1 Mon Sep 17 00:00:00 2001 From: Kara Engelhardt Date: Thu, 13 Aug 2026 13:13:42 +0200 Subject: [PATCH 032/249] Replace deprecated redis setex call --- src/pretix/base/services/quotas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pretix/base/services/quotas.py b/src/pretix/base/services/quotas.py index 124e90e39..6792fd9c9 100644 --- a/src/pretix/base/services/quotas.py +++ b/src/pretix/base/services/quotas.py @@ -193,7 +193,7 @@ class QuotaAvailability: lock_name = '_'.join([str(p) for p in sorted([q.pk for q in quotas])]) if rc.exists(f'quotas:availabilitycachewrite:{lock_name}{self._cache_key_suffix}'): return - rc.setex(f'quotas:availabilitycachewrite:{lock_name}{self._cache_key_suffix}', '1', 10) + rc.set(f'quotas:availabilitycachewrite:{lock_name}{self._cache_key_suffix}', '1', ex=10) update = defaultdict(list) for q in quotas: -- 2.54.0 From c4a5a9a84d72bf9dccf127c9c8feb0757be4cd4d Mon Sep 17 00:00:00 2001 From: Lukas Bockstaller Date: Thu, 13 Aug 2026 17:28:54 +0200 Subject: [PATCH 033/249] BasePaymentProvider & PayPal2: allow to cancel pending payments on a per payment basis (Z#23240966) (#6472) * move payment into pending on PENDING_REVIEW webhook * mark approved payment as pending * extend BasePaymentProvider to gate aborting pending payments on a payment per payment basis * add timeout to paypal after which a pending payment can be canceled * formatting * add missing negation * cleanup abort_pending_allowed methods * Apply suggestions from code review Co-authored-by: pajowu * check all capture elements * rename method and change defaults * remove left over Constant * flake8 . --------- Co-authored-by: pajowu --- src/pretix/base/payment.py | 25 ++- src/pretix/plugins/paypal2/payment.py | 64 ++++++-- src/pretix/plugins/paypal2/signals.py | 2 + .../pretixplugins/paypal2/pending.html | 7 +- src/pretix/plugins/paypal2/views.py | 11 +- src/pretix/presale/views/order.py | 7 +- src/tests/plugins/paypal2/test_webhook.py | 147 ++++++++++++++++++ 7 files changed, 239 insertions(+), 24 deletions(-) diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index c8047bbd5..4dc51c0b4 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -330,9 +330,24 @@ class BasePaymentProvider: payment method. This returns ``False`` by default which is no guarantee that aborting a pending payment can never happen, it just hides the frontend button to avoid users accidentally committing double payments. + If the decision doesn't depend on the specific payment, then only implementing + ``abort_pending_allowed`` is enough, ``payment_abort_pending_allowed(payment: OrderPayment)`` + is expected to take this into account. + As a consumer only evaluate ``payment_abort_pending_allowed(payment: OrderPayment)`` + to check if aborting this pending payment is possible. """ return False + def _payment_abort_pending_allowed(self, payment: OrderPayment) -> bool: + """ + Experimental: This might change during upcomming releases. + Whether or not a user can abort a payment in pending state to switch to another + payment method. This returns ``self.abort_pending_allowed`` by default which is + no guarantee that aborting a pending payment can never happen, it just hides the + frontend button to avoid users accidentally committing double payments. + """ + return self.abort_pending_allowed + @property def requires_invoice_immediately(self): """ @@ -1019,10 +1034,12 @@ class BasePaymentProvider: On success, you should set ``payment.state = OrderPayment.PAYMENT_STATE_CANCELED`` (or call the super method). On failure, you should raise a PaymentException. """ - if payment.state == OrderPayment.PAYMENT_STATE_PENDING and not self.abort_pending_allowed: - raise PaymentException(_( - "This payment is already being processed and can not be canceled any more." - )) + + if payment.state == OrderPayment.PAYMENT_STATE_PENDING: + if not self._payment_abort_pending_allowed(payment): + raise PaymentException(_( + "This payment is already being processed and cannot be canceled any more." + )) payment.state = OrderPayment.PAYMENT_STATE_CANCELED payment.save(update_fields=['state']) diff --git a/src/pretix/plugins/paypal2/payment.py b/src/pretix/plugins/paypal2/payment.py index 31634ce00..08cd341a0 100644 --- a/src/pretix/plugins/paypal2/payment.py +++ b/src/pretix/plugins/paypal2/payment.py @@ -23,7 +23,7 @@ import json import logging import urllib.parse from collections import OrderedDict -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from decimal import Decimal from django import forms @@ -193,6 +193,31 @@ class PaypalSettingsHolder(BasePaymentProvider): } ) )), + ('allow_retries_during_compliance_hold', + forms.BooleanField( + label=_('Allow further payments during compliance hold'), + help_text=_( + 'PayPals fraud prevention might block processing of individual payments for a considerable amount ' + 'of time. The payment is marked as "pending" during this time window. You can allow your customers to ' + 'start another payment attempts during that window. This might result in them being charged twice if the' + 'original payment is approved.' + ), + required=False + )), + ('timeout_payment_during_compliance_hold', + forms.IntegerField( + label=_('Timeout further payment attempts'), + help_text=_( + 'Time duration in minutes after which another payment attempt is possible, while the last payment is ' + 'still under investigation.' + ), + required=False, + widget=forms.NumberInput( + attrs={ + 'data-checkbox-dependency': '#id_payment_paypal_allow_retries_during_compliance_hold', + } + ) + )), ] @@ -515,8 +540,16 @@ class PaypalMethod(BasePaymentProvider): 'XPF': 0, })) - @property - def abort_pending_allowed(self): + def _payment_abort_pending_allowed(self, payment) -> bool: + if not self.settings.get('allow_retries_during_compliance_hold', as_type=bool, default=False): + return False + + if payment.info_data.get('create_time', False): + create_time = datetime.fromisoformat(payment.info_data['create_time']) + duration = self.settings.get('timeout_payment_during_compliance_hold', as_type=int, default=10) + if datetime.now(tz=timezone.utc) - create_time > timedelta(minutes=duration): + return True + return False def _create_paypal_order(self, request, payment=None, cart_total=None): @@ -678,6 +711,8 @@ class PaypalMethod(BasePaymentProvider): else: pp_captured_order = response.result payment.info = json.dumps(pp_captured_order.dict()) + if pp_captured_order.status == 'APPROVED': + payment.state = OrderPayment.PAYMENT_STATE_PENDING payment.save() try: @@ -857,14 +892,20 @@ class PaypalMethod(BasePaymentProvider): logger.info('{}: {} - paypal payment processing time'.format(str(payment.global_id), str(duration))) def payment_pending_render(self, request, payment) -> str: - retry = True + stuck_in_compliance = False + retry = self._payment_abort_pending_allowed(payment) try: - if ( - payment.info - and payment.info_data['purchase_units'][0]['payments']['captures'][0]['status'] == 'PENDING' - ): - retry = False - except (KeyError, IndexError): + for purchase_unit in payment.info_data['purchase_units']: + for capture in purchase_unit['payments']['captures']: + if capture['status'] == "PENDING": + stuck_in_compliance = True + except KeyError: + pass + + try: + if payment.info_data.get('status') == "APPROVED": + stuck_in_compliance = True + except (KeyError): pass error = payment.info_data.get("error", {}) @@ -872,7 +913,8 @@ class PaypalMethod(BasePaymentProvider): template = get_template('pretixplugins/paypal2/pending.html') ctx = {'request': request, 'event': self.event, 'settings': self.settings, - 'retry': retry, 'order': payment.order, 'is_known_issue': is_known_issue} + 'stuck_in_compliance': stuck_in_compliance, 'retry': retry, 'order': payment.order, + 'is_known_issue': is_known_issue} return template.render(ctx) def matching_id(self, payment: OrderPayment): diff --git a/src/pretix/plugins/paypal2/signals.py b/src/pretix/plugins/paypal2/signals.py index 00932625c..0735310c0 100644 --- a/src/pretix/plugins/paypal2/signals.py +++ b/src/pretix/plugins/paypal2/signals.py @@ -166,6 +166,8 @@ def signal_process_response(sender, request: HttpRequest, response: HttpResponse settings_hierarkey.add_default('payment_paypal_debug_buyer_country', '', str) settings_hierarkey.add_default('payment_paypal_method_wallet', True, bool) +settings_hierarkey.add_default('payment_paypal_allow_retries_during_compliance_hold', False, bool) +settings_hierarkey.add_default('payment_paypal_timeout_payment_during_compliance_hold', 10, int) def _nonce(request): diff --git a/src/pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html b/src/pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html index 640bbd4bc..83f1c210c 100644 --- a/src/pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html +++ b/src/pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html @@ -6,9 +6,12 @@ Your payment has failed due to a known issue within PayPal. Please try again, there is a high chance of the payment succeeding on a second or third attempt. You can also try other payment methods, if available. {% endblocktrans %} - {% else %} +{% elif stuck_in_compliance %}

{% blocktrans trimmed %} - Our attempt to execute your payment via PayPal has failed. Please try again or contact us. + Your payment is being processed by PayPal. This takes longer than usual. You can wait until PayPal + acknowledges the payment or you can try paying again with this or another payment method. + This might result in you being charged twice in case PayPal allows your initial payment attempt. + Please contact us to resolve this case. {% endblocktrans %}

{% endif %} {% else %} diff --git a/src/pretix/plugins/paypal2/views.py b/src/pretix/plugins/paypal2/views.py index 7a125e459..0177a6585 100644 --- a/src/pretix/plugins/paypal2/views.py +++ b/src/pretix/plugins/paypal2/views.py @@ -471,8 +471,8 @@ def webhook(request, *args, **kwargs): elif payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED, OrderPayment.PAYMENT_STATE_CANCELED, OrderPayment.PAYMENT_STATE_FAILED): if sale['status'] == 'COMPLETED': - any_captures = False all_captures_completed = True + any_pending_review = False for purchaseunit in sale['purchase_units']: for capture in purchaseunit['payments']['captures']: try: @@ -483,9 +483,9 @@ def webhook(request, *args, **kwargs): if capture['status'] not in ('COMPLETED', 'REFUNDED', 'PARTIALLY_REFUNDED'): all_captures_completed = False - else: - any_captures = True - if any_captures and all_captures_completed: + if capture['status_details']['reason'] == "PENDING_REVIEW": + any_pending_review = True + if all_captures_completed: try: payment.info = json.dumps(sale.dict()) payment.save(update_fields=['info']) @@ -493,6 +493,9 @@ def webhook(request, *args, **kwargs): prov.log_payment_duration(payment) except Quota.QuotaExceededException: pass + if any_pending_review and payment.state != OrderPayment.PAYMENT_STATE_PENDING: + payment.state = OrderPayment.PAYMENT_STATE_PENDING + payment.save(update_fields=['state']) elif sale['status'] == 'APPROVED': try: request.session['payment_paypal_oid'] = payment.info_data['id'] diff --git a/src/pretix/presale/views/order.py b/src/pretix/presale/views/order.py index c13164463..dc130502e 100644 --- a/src/pretix/presale/views/order.py +++ b/src/pretix/presale/views/order.py @@ -349,7 +349,7 @@ class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TicketPageMixin, pp = lp.payment_provider ctx['last_payment_info'] = pp.payment_pending_render(self.request, ctx['last_payment']) - if lp.state == OrderPayment.PAYMENT_STATE_PENDING and not pp.abort_pending_allowed: + if lp.state == OrderPayment.PAYMENT_STATE_PENDING and not pp._payment_abort_pending_allowed(lp): ctx['can_pay'] = False ctx['can_pay'] = ctx['can_pay'] and self.order._can_be_paid() is True @@ -611,7 +611,8 @@ class OrderPayChangeMethod(EventViewMixin, OrderDetailMixin, TemplateView): if self.open_payment: pp = self.open_payment.payment_provider - if self.open_payment.state == OrderPayment.PAYMENT_STATE_PENDING and not pp.abort_pending_allowed: + if self.open_payment.state == OrderPayment.PAYMENT_STATE_PENDING and not pp._payment_abort_pending_allowed( + self.open_payment): messages.error(request, _('A payment is currently pending for this order.')) return redirect(self.get_order_url()) @@ -1718,7 +1719,7 @@ class OrderChangeMixin: if totaldiff > Decimal('0.00') and self.order.status == Order.STATUS_PENDING: for p in self.order.payments.filter(state=OrderPayment.PAYMENT_STATE_PENDING): - if not p.payment_provider.abort_pending_allowed: + if not p.payment_provider._payment_abort_pending_allowed(p): raise OrderError(_('You may not change your order in a way that requires additional payment while ' 'we are processing your current payment. Please check back after your current ' 'payment has been accepted.')) diff --git a/src/tests/plugins/paypal2/test_webhook.py b/src/tests/plugins/paypal2/test_webhook.py index 21eb88853..5fc2cceaf 100644 --- a/src/tests/plugins/paypal2/test_webhook.py +++ b/src/tests/plugins/paypal2/test_webhook.py @@ -244,6 +244,61 @@ def get_test_refund(): } +def get_test_order_review_pending(): + return {'id': '806440346Y391300T', + 'intent': 'CAPTURE', + 'status': 'COMPLETED', + 'purchase_units': [{'reference_id': 'default', + 'amount': {'currency_code': 'EUR', 'value': '43.59'}, + 'payee': {'email_address': 'dummy-facilitator@dummy.dummy', + 'merchant_id': 'G6R2B9YXADKWW'}, + 'description': 'Order JWJGC for PayPal v2', + 'custom_id': 'Order PAYPALV2-JWJGC', + 'soft_descriptor': 'MARTINFACIL', + 'payments': {'captures': [{'id': '22A4162004478570J', + 'status': 'PENDING', + 'status_details': { + 'reason': 'PENDING_REVIEW' + }, + 'amount': {'currency_code': 'EUR', 'value': '43.59'}, + 'final_capture': True, + 'disbursement_mode': 'INSTANT', + 'seller_protection': {'status': 'ELIGIBLE', + 'dispute_categories': [ + 'ITEM_NOT_RECEIVED', + 'UNAUTHORIZED_TRANSACTION']}, + 'seller_receivable_breakdown': { + 'gross_amount': {'currency_code': 'EUR', + 'value': '43.59'}, + 'paypal_fee': {'currency_code': 'EUR', 'value': '1.18'}, + 'net_amount': {'currency_code': 'EUR', + 'value': '42.41'}}, + 'custom_id': 'Order PAYPALV2-JWJGC', + 'links': [{ + 'href': 'https://api.sandbox.paypal.com/v2/payments/captures/22A4162004478570J', + 'rel': 'self', + 'method': 'GET'}, + { + 'href': 'https://api.sandbox.paypal.com/v2/payments/captures/22A4162004478570J/refund', + 'rel': 'refund', + 'method': 'POST'}, + { + 'href': 'https://api.sandbox.paypal.com/v2/checkout/orders/806440346Y391300T', + 'rel': 'up', + 'method': 'GET'}], + 'create_time': '2022-04-28T12:00:22Z', + 'update_time': '2022-04-28T12:00:22Z'}]}}], + 'payer': {'name': {'given_name': 'test', 'surname': 'buyer'}, + 'email_address': 'dummy@dummy.dummy', + 'payer_id': 'Q739JNKWH67HE', + 'address': {'country_code': 'DE'}}, + 'create_time': '2022-04-28T11:59:59Z', + 'update_time': '2022-04-28T12:00:22Z', + 'links': [{'href': 'https://api.sandbox.paypal.com/v2/checkout/orders/806440346Y391300T', + 'rel': 'self', + 'method': 'GET'}]} + + class Object(): pass @@ -690,3 +745,95 @@ def test_webhook_refund2(env, client, monkeypatch): assert r.payment == order.payments.first() assert r.state == OrderRefund.REFUND_STATE_EXTERNAL assert r.source == OrderRefund.REFUND_SOURCE_EXTERNAL + + +@pytest.mark.django_db +def test_webhook_pending_payment(env, client, monkeypatch): + order = env[1] + order.status = Order.STATUS_PENDING + order.save() + with scopes_disabled(): + order.payments.update(state=OrderPayment.PAYMENT_STATE_CREATED) + + pp_order = Result(get_test_order_review_pending()) + mock_orders_get_request = MagicMock(return_value=pp_order) + monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", mock_orders_get_request) + monkeypatch.setattr("pretix.plugins.paypal2.payment.PaypalMethod.init_api", init_api) + with scopes_disabled(): + ReferencedPayPalObject.objects.create(order=order, payment=order.payments.first(), + reference="806440346Y391300T") + + assert order.payments.first().state == OrderPayment.PAYMENT_STATE_CREATED + + client.post('/_paypal/webhook/', json.dumps( + { + "id": "WH-0AH02875JL566842H-2AF03788XV8252724", + "create_time": "2022-04-28T12:00:37.077Z", + "resource_type": "capture", + "event_type": "PAYMENT.CAPTURE.PENDING", + "summary": "Payment pending for € 43.59 EUR", + "resource": { + "update_time": "2022-04-28T12:00:22Z", + "create_time": "2022-04-28T11:59:59Z", + "amount": { + "currency_code": "EUR", + "value": "43.59" + }, + "custom_id": "Order PAYPALV2-JWJGC", + "final_capture": True, + "id": "22A4162004478570J", + "links": [ + { + "href": "https://api.sandbox.paypal.com/v2/payments/captures/5M631111V9599860P", + "method": "GET", + "rel": "self" + }, + { + "href": "https://api.sandbox.paypal.com/v2/payments/captures/5M631111V9599860P/refund", + "method": "POST", + "rel": "refund" + }, + { + "href": "https://api.sandbox.paypal.com/v2/checkout/orders/806440346Y391300T", + "method": "GET", + "rel": "up" + } + ], + "payee": { + "email_address": "sb-ybfun52428692@business.example.com", + "merchant_id": "DLM8QKDR3CFZW" + }, + "seller_protection": { + "status": "NOT_ELIGIBLE" + }, + "status": "PENDING", + "status_details": { + "reason": "PENDING_REVIEW" + }, + "supplementary_data": { + "related_ids": { + "order_id": "9L827155WD164573M" + } + } + }, + "links": [ + { + "href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-0AH02875JL566842H-2AF03788XV8252724", + "method": "GET", + "rel": "self" + }, + { + "href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-0AH02875JL566842H-2AF03788XV8252724/resend", + "method": "POST", + "rel": "resend" + } + ], + "event_version": "1.0", + "resource_version": "2.0" + } + ), content_type='application_json') + + order = env[1] + order.refresh_from_db() + with scopes_disabled(): + assert order.payments.first().state == OrderPayment.PAYMENT_STATE_PENDING -- 2.54.0 From b8b02de283fa7d82ff3ce7df24500b89d628c55f Mon Sep 17 00:00:00 2001 From: Lukas Bockstaller Date: Thu, 13 Aug 2026 18:10:38 +0200 Subject: [PATCH 034/249] paypal change default for allow_retries_during_compliance_hold --- src/pretix/plugins/paypal2/payment.py | 2 +- src/pretix/plugins/paypal2/signals.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pretix/plugins/paypal2/payment.py b/src/pretix/plugins/paypal2/payment.py index 08cd341a0..019bb2d98 100644 --- a/src/pretix/plugins/paypal2/payment.py +++ b/src/pretix/plugins/paypal2/payment.py @@ -541,7 +541,7 @@ class PaypalMethod(BasePaymentProvider): })) def _payment_abort_pending_allowed(self, payment) -> bool: - if not self.settings.get('allow_retries_during_compliance_hold', as_type=bool, default=False): + if not self.settings.get('allow_retries_during_compliance_hold', as_type=bool, default=True): return False if payment.info_data.get('create_time', False): diff --git a/src/pretix/plugins/paypal2/signals.py b/src/pretix/plugins/paypal2/signals.py index 0735310c0..a3bc10d77 100644 --- a/src/pretix/plugins/paypal2/signals.py +++ b/src/pretix/plugins/paypal2/signals.py @@ -166,7 +166,7 @@ def signal_process_response(sender, request: HttpRequest, response: HttpResponse settings_hierarkey.add_default('payment_paypal_debug_buyer_country', '', str) settings_hierarkey.add_default('payment_paypal_method_wallet', True, bool) -settings_hierarkey.add_default('payment_paypal_allow_retries_during_compliance_hold', False, bool) +settings_hierarkey.add_default('payment_paypal_allow_retries_during_compliance_hold', True, bool) settings_hierarkey.add_default('payment_paypal_timeout_payment_during_compliance_hold', 10, int) -- 2.54.0 From 488f7313965af898d5f523830929e70de2d18661 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Fri, 14 Aug 2026 11:24:34 +0200 Subject: [PATCH 035/249] Stripe/Wero: Do not send payment_method_data --- src/pretix/plugins/stripe/payment.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pretix/plugins/stripe/payment.py b/src/pretix/plugins/stripe/payment.py index 46ce909dc..050cb6ef9 100644 --- a/src/pretix/plugins/stripe/payment.py +++ b/src/pretix/plugins/stripe/payment.py @@ -1975,3 +1975,8 @@ class StripeWero(StripeRedirectMethod): 'This payment method is available to European online banking users, whose banking institutions support WERO ' 'either through their native banking apps or through the WERO wallet app. Please have you app ready.' ) + + # Apparently, Stripe cannot process payment_method_data["type"] = "wero" for connect accounts. + # Removing it here (while they figure it out), since it doesn't seem to be doing much anyways. + def _payment_intent_kwargs(self, request, payment): + return {} -- 2.54.0 From 7b1558b22e669517c86d16e63c6e52e25b08e24f Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Fri, 14 Aug 2026 12:07:34 +0200 Subject: [PATCH 036/249] Revert "Stripe/Wero: Do not send payment_method_data" This reverts commit 488f7313965af898d5f523830929e70de2d18661. --- src/pretix/plugins/stripe/payment.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/pretix/plugins/stripe/payment.py b/src/pretix/plugins/stripe/payment.py index 050cb6ef9..46ce909dc 100644 --- a/src/pretix/plugins/stripe/payment.py +++ b/src/pretix/plugins/stripe/payment.py @@ -1975,8 +1975,3 @@ class StripeWero(StripeRedirectMethod): 'This payment method is available to European online banking users, whose banking institutions support WERO ' 'either through their native banking apps or through the WERO wallet app. Please have you app ready.' ) - - # Apparently, Stripe cannot process payment_method_data["type"] = "wero" for connect accounts. - # Removing it here (while they figure it out), since it doesn't seem to be doing much anyways. - def _payment_intent_kwargs(self, request, payment): - return {} -- 2.54.0 From b241adb7d0502db43f60a979b82a4ffbb119367a Mon Sep 17 00:00:00 2001 From: pajowu Date: Fri, 14 Aug 2026 12:34:55 +0200 Subject: [PATCH 037/249] Fix safari blocking widget opening error url in new tab (Z#23243124) (#6477) * Fix safari blocking widget opening error url in new tab (Z#23243124) * Fix autofocus in vue3 widget autofocus no longer works as the elements are added dynamically * fix eslint * rename v-focus to v-autofocus --------- Co-authored-by: Richard Schreiber --- .../static/pretixpresale/js/widget/widget.js | 25 ++++++++-------- .../widget/src/components/Overlay.vue | 30 +++++++++++-------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/pretix/static/pretixpresale/js/widget/widget.js b/src/pretix/static/pretixpresale/js/widget/widget.js index 22c44c8ed..232c9ecd1 100644 --- a/src/pretix/static/pretixpresale/js/widget/widget.js +++ b/src/pretix/static/pretixpresale/js/widget/widget.js @@ -262,7 +262,7 @@ Vue.component('availbox', { }, mounted: function() { if (!this.$root.cart_exists && this.$root.itemnum === 1 && (!this.$root.categories[0].items[0].has_variations || this.$root.categories[0].items[0].variations.length < 2) && !this.$root.has_seating_plan ? 1 : 0) { - this.$refs.quantity.value = 1; + this.$refs.quantity.value = 1; if (this.order_max === 1) { this.$refs.quantity.checked = true; } @@ -310,7 +310,7 @@ Vue.component('availbox', { return this.avail[0] < 100 && this.$root.waiting_list_enabled && this.item.allow_waitinglist; }, waiting_list_url: function () { - var u = this.$root.target_url + 'w/' + widget_id + '/waitinglist/?locale=' + lang + '&item=' + this.item.id + var u = this.$root.target_url + 'w/' + widget_id + '/waitinglist/?locale=' + lang + '&item=' + this.item.id if (this.item.has_variations) { u += '&var=' + this.variation.id } @@ -863,7 +863,7 @@ var shared_alert_fragment = ( '' + '
' + '

{{ $root.error_message }}

' - + '

' + + '

' + '

' + '
' + '' @@ -952,7 +952,7 @@ Vue.component('pretix-overlay', { cancelBlockedClasses: function () { return { 'pretix-widget-visibility-hidden': !this.cancelBlocked, - } + } }, }, mounted () { @@ -974,19 +974,18 @@ Vue.component('pretix-overlay', { this.$root.lightbox.loading = false; }, errorClose: function (e) { - var dialog = e.target; - if (dialog.returnValue == "continue" && this.$root.error_url_after) { - if (this.$root.error_url_after_new_tab) { - window.open(this.$root.error_url_after); - } else if (this.$root.overlay) { - this.$root.overlay.frame_src = this.$root.error_url_after; - this.$root.frame_loading = true; - } - } this.$root.error_message = null; this.$root.error_url_after = null; this.$root.error_url_after_new_tab = false; }, + errorContinue: function () { + if (this.$root.error_url_after_new_tab) { + window.open(this.$root.error_url_after); + } else if (this.$root.overlay) { + this.$root.overlay.frame_src = this.$root.error_url_after; + this.$root.frame_loading = true; + } + }, close: function (e) { if (this.$root.frame_loading) { // Chrome does not allow blocking dialog.cancel event more than once diff --git a/src/pretix/static/pretixpresale/widget/src/components/Overlay.vue b/src/pretix/static/pretixpresale/widget/src/components/Overlay.vue index 4f139fce7..4defa3473 100644 --- a/src/pretix/static/pretixpresale/widget/src/components/Overlay.vue +++ b/src/pretix/static/pretixpresale/widget/src/components/Overlay.vue @@ -54,16 +54,16 @@ function lightboxLoaded () { } } -function errorClose (e: Event) { - const dialog = e.target as HTMLDialogElement - if (dialog.returnValue === 'continue' && store.overlay.errorUrlAfter) { - if (store.overlay.errorUrlAfterNewTab) { - window.open(store.overlay.errorUrlAfter) - } else { - store.overlay.frameSrc = store.overlay.errorUrlAfter - store.overlay.frameLoading = true - } +function errorContinue () { + if (store.overlay.errorUrlAfterNewTab) { + window.open(store.overlay.errorUrlAfter) + } else { + store.overlay.frameSrc = store.overlay.errorUrlAfter + store.overlay.frameLoading = true } +} + +function errorClose () { store.overlay.errorMessage = null store.overlay.errorUrlAfter = null store.overlay.errorUrlAfterNewTab = false @@ -168,6 +168,10 @@ onMounted(() => { onUnmounted(() => { window.removeEventListener('message', onMessage, false) }) + +const vAutofocus = { + mounted: (el) => el.focus() +} diff --git a/src/pretix/static/pretixpresale/widget/src/sharedStore.ts b/src/pretix/static/pretixpresale/widget/src/sharedStore.ts index 00d36c02f..c2273e299 100644 --- a/src/pretix/static/pretixpresale/widget/src/sharedStore.ts +++ b/src/pretix/static/pretixpresale/widget/src/sharedStore.ts @@ -95,6 +95,7 @@ export function createWidgetStore (config: { offset: 0, appendEvents: false, metaFilterFields: [] as MetaFilterField[], + emptyText: null as string | null, // UI state mobile: false, @@ -270,6 +271,7 @@ export function createWidgetStore (config: { this.name = data.name ?? null this.frontpageText = data.frontpage_text ?? null this.metaFilterFields = data.meta_filter_fields ?? [] + this.emptyText = data.empty_text ?? null } else if (data.days !== undefined) { this.days = data.days this.date = null @@ -279,6 +281,7 @@ export function createWidgetStore (config: { this.name = data.name ?? null this.frontpageText = data.frontpage_text ?? null this.metaFilterFields = data.meta_filter_fields ?? [] + this.emptyText = data.empty_text ?? null } else if (data.events !== undefined) { this.events = this.appendEvents && this.events ? this.events.concat(data.events) @@ -290,6 +293,7 @@ export function createWidgetStore (config: { this.frontpageText = data.frontpage_text ?? null this.hasMoreEvents = data.has_more_events ?? false this.metaFilterFields = data.meta_filter_fields ?? [] + this.emptyText = data.empty_text ?? null } else { this.view = 'event' this.targetUrl = data.target_url ?? this.targetUrl -- 2.54.0 From 5af9be6ef0218689a4f39238d9dc4f86e50cc534 Mon Sep 17 00:00:00 2001 From: Albizuri Date: Mon, 24 Aug 2026 13:41:03 +0200 Subject: [PATCH 079/249] Translations: Update Basque Currently translated at 48.2% (3083 of 6387 strings) Translation: pretix/pretix Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/eu/ powered by weblate --- src/pretix/locale/eu/LC_MESSAGES/django.po | 26 +++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/pretix/locale/eu/LC_MESSAGES/django.po b/src/pretix/locale/eu/LC_MESSAGES/django.po index a25d917ac..cf5be8657 100644 --- a/src/pretix/locale/eu/LC_MESSAGES/django.po +++ b/src/pretix/locale/eu/LC_MESSAGES/django.po @@ -8,16 +8,16 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-30 13:52+0000\n" -"PO-Revision-Date: 2026-07-29 11:04+0000\n" +"PO-Revision-Date: 2026-08-24 14:30+0000\n" "Last-Translator: Albizuri \n" -"Language-Team: Basque \n" +"Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "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.7.1\n" +"X-Generator: Weblate 2026.8.1\n" #: pretix/_base_settings.py msgid "English" @@ -6572,10 +6572,8 @@ msgid "This reference will be printed on your invoice for your convenience." msgstr "Erreferentzia hori zure fakturan inprimatuko da, erosoagoa izan dadin." #: pretix/base/models/orders.py -#, fuzzy -#| msgid "Transaction time" msgid "Transmission type" -msgstr "Transakzioaren ordua" +msgstr "Komunikazio kanala" #: pretix/base/models/orders.py #: pretix/plugins/badges/templates/pretixplugins/badges/control_order_position_buttons.html @@ -12242,7 +12240,7 @@ msgid "Order canceled: {code}" msgstr "Eskaera ezeztatua: {code}" #: pretix/base/settings.py -#, fuzzy, python-brace-format +#, python-brace-format msgid "" "Hello,\n" "\n" @@ -12266,7 +12264,7 @@ msgstr "" "{url}\n" "\n" "Agur bero bat,\n" -"PUNTUEUS" +"{event} taldea" #: pretix/base/settings.py #, python-brace-format @@ -13542,15 +13540,13 @@ msgid "Contact:" msgstr "Kontaktua:" #: pretix/base/templates/pretixbase/email/order_details.html -#, fuzzy, python-format -#| msgid "" -#| "You are receiving this email because you placed an order for {event}." +#, python-format msgid "" "You are receiving this email because you placed an order for " "%(event)s." msgstr "" -"Mezu elektroniko hau jasotzen ari zara eskaera bat egin duzulako {event}" -"(e)rako." +"Mezu elektroniko hau jasotzen ari zara eskaera bat egin duzulako %" +"(event)s(e)rako." #: pretix/base/templates/pretixbase/email/order_details.html #: pretix/control/templates/pretixcontrol/organizers/customer.html @@ -13567,7 +13563,7 @@ msgstr "" #: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html #: pretix/presale/templates/pretixpresale/organizers/customer_orders.html msgid "Details" -msgstr "" +msgstr "Xehetasunak" #: pretix/base/templates/pretixbase/email/order_details.html #: pretix/presale/templates/pretixpresale/event/base.html -- 2.54.0 From 44a7bfceabf2867557f0bcbfb3a2a0129c53ba29 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 24 Aug 2026 16:39:26 +0200 Subject: [PATCH 080/249] Update po files [CI skip] Signed-off-by: Raphael Michel --- src/pretix/locale/af/LC_MESSAGES/django.po | 273 +++++++-- src/pretix/locale/ang/LC_MESSAGES/django.po | 273 +++++++-- src/pretix/locale/ar/LC_MESSAGES/django.po | 326 ++++++++--- src/pretix/locale/az/LC_MESSAGES/django.po | 273 +++++++-- src/pretix/locale/ca/LC_MESSAGES/django.po | 314 +++++++--- src/pretix/locale/cs/LC_MESSAGES/django.po | 325 ++++++++--- src/pretix/locale/cy/LC_MESSAGES/django.po | 284 ++++++++-- src/pretix/locale/da/LC_MESSAGES/django.po | 316 ++++++++--- src/pretix/locale/de/LC_MESSAGES/django.po | 348 +++++++++--- .../locale/de_Informal/LC_MESSAGES/django.po | 344 ++++++++--- src/pretix/locale/django.pot | 273 +++++++-- src/pretix/locale/djangojs.pot | 2 +- src/pretix/locale/el/LC_MESSAGES/django.po | 339 ++++++++--- src/pretix/locale/enm/LC_MESSAGES/django.po | 273 +++++++-- src/pretix/locale/es/LC_MESSAGES/django.po | 330 ++++++++--- .../locale/es_419/LC_MESSAGES/django.po | 289 ++++++++-- src/pretix/locale/et/LC_MESSAGES/django.po | 273 +++++++-- src/pretix/locale/eu/LC_MESSAGES/django.po | 324 ++++++++--- src/pretix/locale/fi/LC_MESSAGES/django.po | 314 ++++++++-- src/pretix/locale/fo/LC_MESSAGES/django.po | 281 +++++++-- src/pretix/locale/fr/LC_MESSAGES/django.po | 334 ++++++++--- src/pretix/locale/gl/LC_MESSAGES/django.po | 327 ++++++++--- src/pretix/locale/he/LC_MESSAGES/django.po | 316 +++++++++-- src/pretix/locale/hr/LC_MESSAGES/django.po | 314 ++++++++-- src/pretix/locale/hu/LC_MESSAGES/django.po | 321 +++++++++-- src/pretix/locale/id/LC_MESSAGES/django.po | 331 ++++++++--- src/pretix/locale/it/LC_MESSAGES/django.po | 535 ++++++++++++------ src/pretix/locale/ja/LC_MESSAGES/django.po | 359 +++++++++--- src/pretix/locale/ko/LC_MESSAGES/django.po | 307 ++++++++-- src/pretix/locale/lb/LC_MESSAGES/django.po | 297 ++++++++-- src/pretix/locale/lt/LC_MESSAGES/django.po | 273 +++++++-- src/pretix/locale/lv/LC_MESSAGES/django.po | 305 ++++++++-- src/pretix/locale/nan/LC_MESSAGES/django.po | 273 +++++++-- src/pretix/locale/nb_NO/LC_MESSAGES/django.po | 328 ++++++++--- src/pretix/locale/nl/LC_MESSAGES/django.po | 335 ++++++++--- src/pretix/locale/nl_BE/LC_MESSAGES/django.po | 314 ++++++++-- .../locale/nl_Informal/LC_MESSAGES/django.po | 335 ++++++++--- src/pretix/locale/pl/LC_MESSAGES/django.po | 325 ++++++++--- .../locale/pl_Informal/LC_MESSAGES/django.po | 293 ++++++++-- src/pretix/locale/pt/LC_MESSAGES/django.po | 292 ++++++++-- src/pretix/locale/pt_BR/LC_MESSAGES/django.po | 325 ++++++++--- src/pretix/locale/pt_PT/LC_MESSAGES/django.po | 334 ++++++++--- src/pretix/locale/ro/LC_MESSAGES/django.po | 331 ++++++++--- src/pretix/locale/ru/LC_MESSAGES/django.po | 315 ++++++++--- src/pretix/locale/si/LC_MESSAGES/django.po | 283 +++++++-- src/pretix/locale/sk/LC_MESSAGES/django.po | 320 ++++++++--- src/pretix/locale/sl/LC_MESSAGES/django.po | 309 ++++++++-- src/pretix/locale/sq/LC_MESSAGES/django.po | 279 +++++++-- src/pretix/locale/sv/LC_MESSAGES/django.po | 324 ++++++++--- src/pretix/locale/th/LC_MESSAGES/django.po | 304 ++++++++-- src/pretix/locale/tr/LC_MESSAGES/django.po | 334 ++++++++--- src/pretix/locale/uk/LC_MESSAGES/django.po | 324 ++++++++--- src/pretix/locale/vi/LC_MESSAGES/django.po | 319 +++++++++-- src/pretix/locale/vls/LC_MESSAGES/django.po | 273 +++++++-- .../locale/zh_Hans/LC_MESSAGES/django.po | 335 ++++++++--- .../locale/zh_Hant/LC_MESSAGES/django.po | 320 +++++++++-- 56 files changed, 13813 insertions(+), 3504 deletions(-) diff --git a/src/pretix/locale/af/LC_MESSAGES/django.po b/src/pretix/locale/af/LC_MESSAGES/django.po index 325594947..f55515c0e 100644 --- a/src/pretix/locale/af/LC_MESSAGES/django.po +++ b/src/pretix/locale/af/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: 2026-07-15 14:20+0000\n" "Last-Translator: Llewelyn Barkhuizen \n" "Language-Team: Afrikaans \n" "Language-Team: Arabic \n" "Language-Team: Azerbaijani \n" "Language-Team: Catalan \n" "Language-Team: Czech \n" "Language-Team: Welsh \n" -"Language-Team: Danish \n" +"Language-Team: Danish \n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -87,6 +87,10 @@ msgstr "Græsk" msgid "Hebrew" msgstr "hebræisk" +#: pretix/_base_settings.py +msgid "Hungarian" +msgstr "" + #: pretix/_base_settings.py msgid "Indonesian" msgstr "Indonesisk" @@ -429,6 +433,12 @@ msgstr "Medie er tilknyttet et andet arrangement" msgid "You cannot exchange a medium for a medium." msgstr "Du kan ikke bytte en medium mod en anden medium." +#: pretix/api/views/checkin.py +#, fuzzy +#| msgid "Product does not support medium exchange." +msgid "You cannot simulate a medium exchange." +msgstr "Produktet understøtter ikke udskiftning af medier." + #: pretix/api/views/oauth.py pretix/control/logdisplay.py #, python-brace-format msgid "" @@ -878,8 +888,7 @@ msgstr "Oplysninger om begivenhed eller dato" #: pretix/control/forms/organizer.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/organizers/customer.html -#: pretix/control/views/item.py pretix/plugins/badges/exporters.py -#: pretix/plugins/checkinlists/exporters.py +#: pretix/plugins/badges/exporters.py pretix/plugins/checkinlists/exporters.py #: pretix/plugins/ticketoutputpdf/exporters.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html #: pretix/presale/templates/pretixpresale/event/fragment_change_form.html @@ -896,7 +905,6 @@ msgstr "Deltager" #: pretix/base/forms/questions.py pretix/base/models/customers.py #: pretix/base/models/orders.py pretix/base/pdf.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "Attendee email" msgstr "E-mailadresse" @@ -1644,9 +1652,8 @@ msgid "Invoice sender:" msgstr "Fakturaafsender:" #: pretix/base/exporters/invoices.py pretix/base/exporters/orderlist.py -#: pretix/base/forms/questions.py pretix/base/modelimport_orders.py -#: pretix/base/models/customers.py pretix/base/models/orders.py -#: pretix/control/forms/filter.py +#: pretix/base/modelimport_orders.py pretix/base/models/customers.py +#: pretix/base/models/orders.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html @@ -1658,8 +1665,7 @@ msgstr "Adresse" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "ZIP code" msgstr "Postnummer" @@ -1667,8 +1673,7 @@ msgstr "Postnummer" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "City" msgstr "By" @@ -1691,8 +1696,7 @@ msgstr "Stat / region" #: pretix/base/settings.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/email/login_notice.txt #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Country" @@ -1720,8 +1724,7 @@ msgstr "Fakturamodtager:" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Company" @@ -2534,7 +2537,8 @@ msgstr "Status på betalinger" msgid "Refund states" msgstr "Status på tilbagebetalinger" -#: pretix/base/exporters/orderlist.py pretix/base/models/orders.py +#: pretix/base/exporters/orderlist.py pretix/base/models/items.py +#: pretix/base/models/orders.py #: pretix/control/templates/pretixcontrol/datasync/failed_jobs.html #: pretix/control/templates/pretixcontrol/organizers/customer.html #: pretix/control/templates/pretixcontrol/organizers/outgoing_mail.html @@ -3053,16 +3057,13 @@ msgstr "" "eller den var beskadiget." #: pretix/base/forms/questions.py -msgid "" -"If you keep this empty, the ticket will be valid starting at the time of " -"purchase." -msgstr "" -"Hvis du lader dette felt stå tomt, vil billetten være gyldig direkte efter " -"køb." +#, fuzzy +msgid "Street" +msgstr "Afsenderadresse" #: pretix/base/forms/questions.py -msgid "Street and Number" -msgstr "Gadenavn og nummer" +msgid "State" +msgstr "Stat eller region" #: pretix/base/forms/questions.py #, python-brace-format @@ -3096,6 +3097,18 @@ msgstr "" msgid "Please enter a date and time no later than {max}." msgstr "Please enter a date and time no later than {max}." +#: pretix/base/forms/questions.py +msgid "" +"If you keep this empty, the ticket will be valid starting at the time of " +"purchase." +msgstr "" +"Hvis du lader dette felt stå tomt, vil billetten være gyldig direkte efter " +"køb." + +#: pretix/base/forms/questions.py +msgid "Street and Number" +msgstr "Gadenavn og nummer" + #: pretix/base/forms/questions.py msgid "" "Optional, but depending on the country you reside in we might need to charge " @@ -5704,6 +5717,10 @@ msgstr "Den valgte variation tilhører ikke dette arrangement." msgid "The count needs to be equal to or greater than zero." msgstr "Antallet skal være lig med eller større end nul." +#: pretix/base/models/items.py pretix/base/models/orders.py +msgid "Order position" +msgstr "Bestillingspost" + #: pretix/base/models/items.py msgid "Number" msgstr "Antal" @@ -5741,6 +5758,10 @@ msgstr "Dato og tidspunkt" msgid "Country code (ISO 3166-1 alpha-2)" msgstr "Landekode (ISO 3166-1 alpha-2)" +#: pretix/base/models/items.py +msgid "Asked on" +msgstr "" + #: pretix/base/models/items.py pretix/base/models/organizer.py msgid "" "The identifier may only contain letters, numbers, dots, dashes, and " @@ -6396,10 +6417,6 @@ msgstr "Gavekort" msgid "Value" msgstr "Værdi" -#: pretix/base/models/orders.py -msgid "Order position" -msgstr "Bestillingspost" - #: pretix/base/models/orders.py msgid "Cart ID (e.g. session key)" msgstr "Kurv-id (fx sessionsnøgle)" @@ -7424,8 +7441,11 @@ msgid "The payment for this invoice has already been received." msgstr "Betalingen for denne faktura er allerede modtaget." #: pretix/base/payment.py +#, fuzzy +#| msgid "" +#| "This payment is already being processed and can not be canceled any more." msgid "" -"This payment is already being processed and can not be canceled any more." +"This payment is already being processed and cannot be canceled any more." msgstr "" "Denne betaling er allerede under behandling og kan ikke længere annulleres." @@ -8195,6 +8215,18 @@ msgstr "Forsalgs starttidspunkt" msgid "Presale end" msgstr "Forsalgs sluttidspunkt" +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order email" +msgid "Order creation" +msgstr "Bestillings-e-mail" + +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order expired" +msgid "Order expiry" +msgstr "Bestilling udløbet" + #: pretix/base/reldate.py msgid "before" msgstr "før" @@ -8219,6 +8251,36 @@ msgstr "Relativt tidspunkt:" msgid "Not set" msgstr "Ikke sat" +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'before' for '{}'" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'after' for '{}'" +msgstr "" + #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" msgstr "Tilfældigt (standard, virker med alle Pretix-apps)" @@ -9488,7 +9550,7 @@ msgstr "Beløbet er blevet trukket på dit kort." msgid "Please transfer money to this bank account: 9999-9999-9999-9999" msgstr "Overfør venligst beløbet til denne bankkonto: 9999-9999-9999-9999" -#: pretix/base/services/placeholders.py pretix/control/views/organizer.py +#: pretix/base/services/placeholders.py msgid "This value will be replaced based on dynamic parameters." msgstr "Denne værdi vil blive erstattet på baggrund af dynamiske parametre." @@ -10196,6 +10258,25 @@ msgstr "" "for dette arrangement er afsluttet. Du kan bruge den til at beskrive andre " "muligheder for at få en billet, f.eks. et billetkontor." +#: pretix/base/settings.py +msgid "No dates match your criteria." +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "Text for empty date results" +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "" +"This text will be shown if the calendar or list of dates is empty, e.g. " +"because a month does not contain any dates or a filter chosen by the user " +"does not find any results. You can use this to advertise ways to get in " +"touch with you to arrange further dates. We do not recommend more than one " +"or two sentences." +msgstr "" + #: pretix/base/settings.py msgid "Guidance text" msgstr "Vejledende tekst" @@ -14120,6 +14201,28 @@ msgstr "" "Du har konfigureret vouchers til at være gyldige i {} år plus det år, hvor " "voucheren udstedes." +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment date" +msgid "Payment term" +msgstr "Betalingsdato" + +#: pretix/control/forms/event.py +msgid "same as above" +msgstr "" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in days" +msgid "different payment term in days" +msgstr "Betalingsfrist i dage" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in minutes" +msgid "different payment term in minutes" +msgstr "Betalingsfrist i minutter" + #: pretix/control/forms/event.py msgid "Prices including tax" msgstr "Priser inkl. moms" @@ -20841,6 +20944,7 @@ msgid "Top recommendation" msgstr "" #: pretix/control/templates/pretixcontrol/event/plugins.html +#: pretix/control/templates/pretixcontrol/items/questions.html #: pretix/control/templates/pretixcontrol/organizers/plugins.html #, fuzzy msgid "Experimental feature" @@ -22620,6 +22724,30 @@ msgid "" "dietary requirements." msgstr "" +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new question" +msgid "Create a new per-ticket question" +msgstr "Opret spørgsmål" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new question" +msgid "Create a new order-level question" +msgstr "Opret spørgsmål" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Delete question" +msgid "Per-ticket questions" +msgstr "Sket spørgsmål" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"These questions are asked for every ticket, so possibly multiple times in " +"the same order." +msgstr "" + #: pretix/control/templates/pretixcontrol/items/questions.html msgid "Create a new question" msgstr "Opret spørgsmål" @@ -22638,6 +22766,29 @@ msgstr "" msgid "All personalized products" msgstr "Alle produkter" +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Include questions" +msgid "Per-order questions" +msgstr "Medtag spørgsmål" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"This functionality is in active development and expected to change " +"significantly over the coming months." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"Per-order questions are currently not supported and will not be displayed in " +"pretixPOS." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +msgid "These questions are asked once per order." +msgstr "Spørgsmålet er blevet slettet." + #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html #, python-format @@ -23207,6 +23358,14 @@ msgstr "Faktureringsoplysninger" msgid "(optional)" msgstr "(valgfri)" +#: pretix/control/templates/pretixcontrol/order/change_questions.html +#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html +#: pretix/presale/templates/pretixpresale/event/checkout_questions.html +#: pretix/presale/templates/pretixpresale/event/order_modify.html +#, fuzzy +msgid "Additional order information" +msgstr "Metaoplysninger" + #: pretix/control/templates/pretixcontrol/order/delete.html #, fuzzy msgid "Delete order" @@ -23241,6 +23400,23 @@ msgstr "Ja, tilbagebetal bestilling" msgid "Extend payment term" msgstr "Udvid betalingsfrist" +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "" +"This file has been uploaded by a user and could contain viruses or other " +"malicious content." +msgstr "" + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "UNSAFE" +msgstr "USIKKER" + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +#: pretix/control/templates/pretixcontrol/order/index.html +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "not answered" +msgstr "ikke besvaret" + #: pretix/control/templates/pretixcontrol/order/index.html #, python-format msgid "Order details: %(code)s" @@ -23520,25 +23696,10 @@ msgstr "Book fra %(start_date)s" msgid "Ticket page" msgstr "Billetnavn" -#: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "not answered" -msgstr "ikke besvaret" - #: pretix/control/templates/pretixcontrol/order/index.html msgid "This question will be asked during check-in." msgstr "" -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "" -"This file has been uploaded by a user and could contain viruses or other " -"malicious content." -msgstr "" - -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "UNSAFE" -msgstr "USIKKER" - #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/orders/overview.html #: pretix/plugins/reports/accountingreport.py @@ -24907,6 +25068,16 @@ msgstr "" msgid "Hardware model" msgstr "" +#: pretix/control/templates/pretixcontrol/organizers/devices.html +#, fuzzy, python-format +#| msgid "Begin: %(time)s" +msgid "Last seen: %(time)s" +msgstr "Start: %(time)s" + +#: pretix/control/templates/pretixcontrol/organizers/devices.html +msgid "No recent contact" +msgstr "" + #: pretix/control/templates/pretixcontrol/organizers/devices.html #, fuzzy msgid "Not yet initialized" @@ -28049,11 +28220,6 @@ msgstr "Rækkefølgen af kategorier er blevet ændret." msgid "Not all objects have been selected." msgstr "Spørgsmålet er blevet slettet." -#: pretix/control/views/item.py -#, fuzzy -msgid "Street" -msgstr "Afsenderadresse" - #: pretix/control/views/item.py msgid "The requested question does not exist." msgstr "" @@ -28204,7 +28370,7 @@ msgstr "" #: pretix/control/views/mailsetup.py msgid "" -"We did not find DMARC record for your domain. This means that there is a " +"We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" @@ -28638,7 +28804,8 @@ msgstr "Emne: {subject}" msgid "Your message has been queued and will be sent to {}." msgstr "Din besked vil blive sendt til {}." -#: pretix/control/views/orders.py pretix/presale/views/order.py +#: pretix/control/views/orders.py pretix/presale/views/cart.py +#: pretix/presale/views/order.py msgid "" "This link is no longer valid. Please go back, refresh the page, and try " "again." @@ -30880,6 +31047,29 @@ msgid "" "getting your money right away." msgstr "" +#: pretix/plugins/paypal2/payment.py +msgid "Allow further payments during compliance hold" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"PayPals fraud prevention might block processing of individual payments for a " +"considerable amount of time. The payment is marked as \"pending\" during " +"this time window. You can allow your customers to start another payment " +"attempts during that window. This might result in them being charged twice " +"if theoriginal payment is approved." +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "Timeout further payment attempts" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"Time duration in minutes after which another payment attempt is possible, " +"while the last payment is still under investigation." +msgstr "" + #: pretix/plugins/paypal2/payment.py #, fuzzy msgid "-- Automatic --" @@ -31127,8 +31317,11 @@ msgstr "Vælg venligst hvordan du vil betale." #: pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html msgid "" -"Our attempt to execute your payment via PayPal has failed. Please try again " -"or contact us." +"Your payment is being processed by PayPal. This takes longer than usual. You " +"can wait until PayPal acknowledges the payment or you can try paying again " +"with this or another payment method. This might result in you being charged " +"twice in case PayPal allows your initial payment attempt. Please contact us " +"to resolve this case." msgstr "" #: pretix/plugins/paypal2/views.py @@ -34168,10 +34361,6 @@ msgstr[1] "Denne bestilling er allerede blevet tilbagebetalt." msgid "No attendee name provided" msgstr "Vis deltagernavne på fakturaer" -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "The image you previously uploaded" -msgstr "" - #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "The price of this product was reduced because of an automatic discount." msgstr "" @@ -34597,6 +34786,10 @@ msgstr "Samlet værdi" msgid "Set price in %(currency)s for %(item)s" msgstr "" +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "The image you previously uploaded" +msgstr "" + #: pretix/presale/templates/pretixpresale/event/fragment_quota_left.html #, python-format msgid "%(num)s currently available" @@ -36461,9 +36654,6 @@ msgstr "Kosovo" #~ msgid "Please do not use special characters in names." #~ msgstr "Brug venligst ikke specialtegn i navne." -#~ msgid "State" -#~ msgstr "Stat eller region" - #~ msgid "Program times: date and time" #~ msgstr "Programtider: dato og klokkeslæt" diff --git a/src/pretix/locale/de/LC_MESSAGES/django.po b/src/pretix/locale/de/LC_MESSAGES/django.po index 4403d8ba9..0b9d9d821 100644 --- a/src/pretix/locale/de/LC_MESSAGES/django.po +++ b/src/pretix/locale/de/LC_MESSAGES/django.po @@ -4,11 +4,11 @@ msgid "" msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: 2026-07-30 18:31+0000\n" "Last-Translator: Raphael Michel \n" -"Language-Team: German \n" +"Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -89,6 +89,10 @@ msgstr "Griechisch" msgid "Hebrew" msgstr "Hebräisch" +#: pretix/_base_settings.py +msgid "Hungarian" +msgstr "" + #: pretix/_base_settings.py msgid "Indonesian" msgstr "Indonesisch" @@ -429,6 +433,12 @@ msgstr "Medium ist mit anderer Veranstaltung verbunden" msgid "You cannot exchange a medium for a medium." msgstr "Sie können kein Medium gegen ein Medium eintauschen." +#: pretix/api/views/checkin.py +#, fuzzy +#| msgid "Product does not support medium exchange." +msgid "You cannot simulate a medium exchange." +msgstr "Das Produkt erlaubt keinen Umtausch in ein Medium." + #: pretix/api/views/oauth.py pretix/control/logdisplay.py #, python-brace-format msgid "" @@ -883,8 +893,7 @@ msgstr "Veranstaltungs- oder Termininformationen" #: pretix/control/forms/organizer.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/organizers/customer.html -#: pretix/control/views/item.py pretix/plugins/badges/exporters.py -#: pretix/plugins/checkinlists/exporters.py +#: pretix/plugins/badges/exporters.py pretix/plugins/checkinlists/exporters.py #: pretix/plugins/ticketoutputpdf/exporters.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html #: pretix/presale/templates/pretixpresale/event/fragment_change_form.html @@ -901,7 +910,6 @@ msgstr "Teilnehmer*in" #: pretix/base/forms/questions.py pretix/base/models/customers.py #: pretix/base/models/orders.py pretix/base/pdf.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "Attendee email" msgstr "E-Mail Teilnehmer*in" @@ -1648,9 +1656,8 @@ msgid "Invoice sender:" msgstr "Rechnungsabsender:" #: pretix/base/exporters/invoices.py pretix/base/exporters/orderlist.py -#: pretix/base/forms/questions.py pretix/base/modelimport_orders.py -#: pretix/base/models/customers.py pretix/base/models/orders.py -#: pretix/control/forms/filter.py +#: pretix/base/modelimport_orders.py pretix/base/models/customers.py +#: pretix/base/models/orders.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html @@ -1662,8 +1669,7 @@ msgstr "Adresse" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "ZIP code" msgstr "Postleitzahl" @@ -1671,8 +1677,7 @@ msgstr "Postleitzahl" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "City" msgstr "Stadt" @@ -1695,8 +1700,7 @@ msgstr "Bundesstaat" #: pretix/base/settings.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/email/login_notice.txt #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Country" @@ -1724,8 +1728,7 @@ msgstr "Rechnungsempfänger:" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Company" @@ -2541,7 +2544,8 @@ msgstr "Zahlungsstatus" msgid "Refund states" msgstr "Erstattungsstatus" -#: pretix/base/exporters/orderlist.py pretix/base/models/orders.py +#: pretix/base/exporters/orderlist.py pretix/base/models/items.py +#: pretix/base/models/orders.py #: pretix/control/templates/pretixcontrol/datasync/failed_jobs.html #: pretix/control/templates/pretixcontrol/organizers/customer.html #: pretix/control/templates/pretixcontrol/organizers/outgoing_mail.html @@ -3063,15 +3067,12 @@ msgid "" msgstr "Die hochgeladene Datei ist kein Bild oder ist beschädigt." #: pretix/base/forms/questions.py -msgid "" -"If you keep this empty, the ticket will be valid starting at the time of " -"purchase." -msgstr "" -"Wenn Sie dieses Feld leer lassen, ist das Ticket ab dem Kaufzeitpunkt gültig." +msgid "Street" +msgstr "Straße" #: pretix/base/forms/questions.py -msgid "Street and Number" -msgstr "Straße und Hausnummer" +msgid "State" +msgstr "Zustand" #: pretix/base/forms/questions.py #, python-brace-format @@ -3103,6 +3104,17 @@ msgstr "Bitte geben Sie einen Zeitpunkt nicht vor {min} ein." msgid "Please enter a date and time no later than {max}." msgstr "Bitte geben Sie einen Zeitpunkt nicht nach {max} ein." +#: pretix/base/forms/questions.py +msgid "" +"If you keep this empty, the ticket will be valid starting at the time of " +"purchase." +msgstr "" +"Wenn Sie dieses Feld leer lassen, ist das Ticket ab dem Kaufzeitpunkt gültig." + +#: pretix/base/forms/questions.py +msgid "Street and Number" +msgstr "Straße und Hausnummer" + #: pretix/base/forms/questions.py msgid "" "Optional, but depending on the country you reside in we might need to charge " @@ -5746,6 +5758,10 @@ msgstr "Diese Variante gehört nicht zu diesem Produkt." msgid "The count needs to be equal to or greater than zero." msgstr "Die Anzahl muss mindestens null sein." +#: pretix/base/models/items.py pretix/base/models/orders.py +msgid "Order position" +msgstr "Bestellposition" + #: pretix/base/models/items.py msgid "Number" msgstr "Zahl" @@ -5783,6 +5799,13 @@ msgstr "Datum und Uhrzeit" msgid "Country code (ISO 3166-1 alpha-2)" msgstr "Land (ISO3166-1-alpha-2-Ländercode)" +#: pretix/base/models/items.py +#, fuzzy +#| msgctxt "attendee_data" +#| msgid "Asked" +msgid "Asked on" +msgstr "Abfragen" + #: pretix/base/models/items.py pretix/base/models/organizer.py msgid "" "The identifier may only contain letters, numbers, dots, dashes, and " @@ -6445,10 +6468,6 @@ msgstr "Wertgutschein / Geschenkgutschein" msgid "Value" msgstr "Wert" -#: pretix/base/models/orders.py -msgid "Order position" -msgstr "Bestellposition" - #: pretix/base/models/orders.py msgid "Cart ID (e.g. session key)" msgstr "Warenkorb-ID (z.B. Session-ID)" @@ -7487,8 +7506,11 @@ msgid "The payment for this invoice has already been received." msgstr "Die Rechnung wurde bereits beglichen." #: pretix/base/payment.py +#, fuzzy +#| msgid "" +#| "This payment is already being processed and can not be canceled any more." msgid "" -"This payment is already being processed and can not be canceled any more." +"This payment is already being processed and cannot be canceled any more." msgstr "" "Diese Zahlung wird bereits verarbeitet und kann nicht mehr abgebrochen " "werden." @@ -8267,6 +8289,18 @@ msgstr "Start des Vorverkaufs" msgid "Presale end" msgstr "Ende des Vorverkaufs" +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order email" +msgid "Order creation" +msgstr "E-Mail-Adresse der Bestellung" + +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order expired" +msgid "Order expiry" +msgstr "Bestellung abgelaufen" + #: pretix/base/reldate.py msgid "before" msgstr "bevor" @@ -8291,6 +8325,36 @@ msgstr "Relativ:" msgid "Not set" msgstr "Nicht gesetzt" +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'before' for '{}'" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'after' for '{}'" +msgstr "" + #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" msgstr "Zufällig (Standard, funktioniert mit allen pretix-Apps)" @@ -9591,7 +9655,7 @@ msgid "Please transfer money to this bank account: 9999-9999-9999-9999" msgstr "" "Bitte überweisen Sie den vollen Betrag auf das Bankkonto 9999-9999-9999-" -#: pretix/base/services/placeholders.py pretix/control/views/organizer.py +#: pretix/base/services/placeholders.py msgid "This value will be replaced based on dynamic parameters." msgstr "" "Dieser Textbaustein wird aufgrund verschiedener Parameter dynamisch " @@ -10306,6 +10370,25 @@ msgstr "" "Verkaufszeitraum beendet ist. Sie können hier andere Wege ein Ticket zu " "bekommen, wie z.B. eine Abendkasse, beschreiben." +#: pretix/base/settings.py +msgid "No dates match your criteria." +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "Text for empty date results" +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "" +"This text will be shown if the calendar or list of dates is empty, e.g. " +"because a month does not contain any dates or a filter chosen by the user " +"does not find any results. You can use this to advertise ways to get in " +"touch with you to arrange further dates. We do not recommend more than one " +"or two sentences." +msgstr "" + #: pretix/base/settings.py msgid "Guidance text" msgstr "Hilfetext" @@ -14287,6 +14370,30 @@ msgstr "" "Sie haben eingestellt, dass Wertgutscheine {} Jahre nach Ende des Jahres der " "Ausstellung ablaufen." +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment date" +msgid "Payment term" +msgstr "Zahlungsdatum" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "(Same as above)" +msgid "same as above" +msgstr "(wie oben)" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in days" +msgid "different payment term in days" +msgstr "Zahlungsziel in Tagen" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in minutes" +msgid "different payment term in minutes" +msgstr "Zahlungsziel in Minuten" + #: pretix/control/forms/event.py msgid "Prices including tax" msgstr "Preis inklusive Steuern" @@ -16850,8 +16957,8 @@ msgid "" "%(max_redeemed)s times." msgstr "" "Sie können die maximale Anzahl Einlösungen nicht auf %(max_usages)s " -"reduzieren, weil mindestens einer der gewählten Gutscheine bereits %" -"(max_redeemed)s mal eingelöst wurde." +"reduzieren, weil mindestens einer der gewählten Gutscheine bereits " +"%(max_redeemed)s mal eingelöst wurde." #: pretix/control/forms/vouchers.py msgid "" @@ -21204,6 +21311,7 @@ msgid "Top recommendation" msgstr "Empfohlene Erweiterung" #: pretix/control/templates/pretixcontrol/event/plugins.html +#: pretix/control/templates/pretixcontrol/items/questions.html #: pretix/control/templates/pretixcontrol/organizers/plugins.html msgid "Experimental feature" msgstr "Experimentelle Funktion" @@ -23115,6 +23223,30 @@ msgstr "" "auszufüllen. Wenn Ihre Veranstaltung Verpflegung beinhaltet, könnten Sie " "z.B. nach Allergien Ihrer Teilnehmer fragen." +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new question" +msgid "Create a new per-ticket question" +msgstr "Neue Frage erstellen" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new question" +msgid "Create a new order-level question" +msgstr "Neue Frage erstellen" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Delete question" +msgid "Per-ticket questions" +msgstr "Frage löschen" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"These questions are asked for every ticket, so possibly multiple times in " +"the same order." +msgstr "" + #: pretix/control/templates/pretixcontrol/items/questions.html msgid "Create a new question" msgstr "Neue Frage erstellen" @@ -23131,6 +23263,30 @@ msgstr "Beim Check-in fragen" msgid "All personalized products" msgstr "Alle personalisierten Produkte" +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Include questions" +msgid "Per-order questions" +msgstr "Fragen anzeigen" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"This functionality is in active development and expected to change " +"significantly over the coming months." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"Per-order questions are currently not supported and will not be displayed in " +"pretixPOS." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "The question has been reordered." +msgid "These questions are asked once per order." +msgstr "Die Frage wurde umsortiert." + #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html #, python-format @@ -23735,6 +23891,15 @@ msgstr "Rechnungsinformationen" msgid "(optional)" msgstr "(optional)" +#: pretix/control/templates/pretixcontrol/order/change_questions.html +#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html +#: pretix/presale/templates/pretixpresale/event/checkout_questions.html +#: pretix/presale/templates/pretixpresale/event/order_modify.html +#, fuzzy +#| msgid "Additional information" +msgid "Additional order information" +msgstr "Zusätzliche Informationen" + #: pretix/control/templates/pretixcontrol/order/delete.html msgid "Delete order" msgstr "Bestellung löschen" @@ -23764,6 +23929,25 @@ msgstr "Ja, Bestellung ablehnen" msgid "Extend payment term" msgstr "Zahlungsfrist verändern" +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "" +"This file has been uploaded by a user and could contain viruses or other " +"malicious content." +msgstr "" +"Diese Datei wurde von einem Nutzer hochgeladen und kann Viren oder anderen " +"schädlichen Inhalt enthalten." + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "UNSAFE" +msgstr "UNSICHER" + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +#: pretix/control/templates/pretixcontrol/order/index.html +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "not answered" +msgstr "nicht beantwortet" + #: pretix/control/templates/pretixcontrol/order/index.html #, python-format msgid "Order details: %(code)s" @@ -24048,27 +24232,10 @@ msgstr "Gültig %(datetime_range)s" msgid "Ticket page" msgstr "Ticket-Seite" -#: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "not answered" -msgstr "nicht beantwortet" - #: pretix/control/templates/pretixcontrol/order/index.html msgid "This question will be asked during check-in." msgstr "Diese Frage wird beim Check-In gestellt." -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "" -"This file has been uploaded by a user and could contain viruses or other " -"malicious content." -msgstr "" -"Diese Datei wurde von einem Nutzer hochgeladen und kann Viren oder anderen " -"schädlichen Inhalt enthalten." - -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "UNSAFE" -msgstr "UNSICHER" - #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/orders/overview.html #: pretix/plugins/reports/accountingreport.py @@ -25440,6 +25607,16 @@ msgstr "Neues Gerät verbinden" msgid "Hardware model" msgstr "Hardware-Modell" +#: pretix/control/templates/pretixcontrol/organizers/devices.html +#, fuzzy, python-format +#| msgid "Begin: %(time)s" +msgid "Last seen: %(time)s" +msgstr "Beginn: %(time)s Uhr" + +#: pretix/control/templates/pretixcontrol/organizers/devices.html +msgid "No recent contact" +msgstr "" + #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "Not yet initialized" msgstr "Noch nicht installiert" @@ -28735,10 +28912,6 @@ msgstr "Die Reihenfolge der Kategorien wurde gespeichert." msgid "Not all objects have been selected." msgstr "Es wurden nicht alle Objekte ausgewählt." -#: pretix/control/views/item.py -msgid "Street" -msgstr "Straße" - #: pretix/control/views/item.py msgid "The requested question does not exist." msgstr "Die ausgewählte Frage existiert nicht." @@ -28912,8 +29085,13 @@ msgstr "" "Einstellungen Ihrer Domain anpassen." #: pretix/control/views/mailsetup.py +#, fuzzy +#| msgid "" +#| "We did not find DMARC record for your domain. This means that there is a " +#| "very high chance most of the emails will be rejected or marked as spam. " +#| "You should update the DNS settings of your domain." msgid "" -"We did not find DMARC record for your domain. This means that there is a " +"We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" @@ -29377,7 +29555,8 @@ msgstr "Betreff: {subject}" msgid "Your message has been queued and will be sent to {}." msgstr "Die Nachricht wurde gespeichert und wird an {} versendet." -#: pretix/control/views/orders.py pretix/presale/views/order.py +#: pretix/control/views/orders.py pretix/presale/views/cart.py +#: pretix/presale/views/order.py msgid "" "This link is no longer valid. Please go back, refresh the page, and try " "again." @@ -31658,6 +31837,29 @@ msgstr "" "Limit) in mehreren Raten oder binnen 30 Tagen zu bezahlen. Sie, als der " "Händler, erhalten Ihr Geld jedoch sofort." +#: pretix/plugins/paypal2/payment.py +msgid "Allow further payments during compliance hold" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"PayPals fraud prevention might block processing of individual payments for a " +"considerable amount of time. The payment is marked as \"pending\" during " +"this time window. You can allow your customers to start another payment " +"attempts during that window. This might result in them being charged twice " +"if theoriginal payment is approved." +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "Timeout further payment attempts" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"Time duration in minutes after which another payment attempt is possible, " +"while the last payment is still under investigation." +msgstr "" + #: pretix/plugins/paypal2/payment.py msgid "-- Automatic --" msgstr "-- Automatisch --" @@ -31928,11 +32130,12 @@ msgstr "Bitte wählen Sie die gewünschte Zahlungsmethode aus." #: pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html msgid "" -"Our attempt to execute your payment via PayPal has failed. Please try again " -"or contact us." +"Your payment is being processed by PayPal. This takes longer than usual. You " +"can wait until PayPal acknowledges the payment or you can try paying again " +"with this or another payment method. This might result in you being charged " +"twice in case PayPal allows your initial payment attempt. Please contact us " +"to resolve this case." msgstr "" -"Unser Versuch, die Zahlung mit PayPal auszuführen, ist gescheitert. Bitte " -"probieren Sie es erneut oder kontaktieren Sie uns." #: pretix/plugins/paypal2/views.py msgid "" @@ -34989,10 +35192,6 @@ msgstr[1] "Das Ticket wurde %(count)s-mal eingelöst." msgid "No attendee name provided" msgstr "Name der teilnehmenden Person nicht angegeben" -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "The image you previously uploaded" -msgstr "Das zuvor hochgeladene Bild" - #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "The price of this product was reduced because of an automatic discount." msgstr "" @@ -35434,6 +35633,10 @@ msgstr "%(value)s netto" msgid "Set price in %(currency)s for %(item)s" msgstr "Preis in %(currency)s für %(item)s setzen" +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "The image you previously uploaded" +msgstr "Das zuvor hochgeladene Bild" + #: pretix/presale/templates/pretixpresale/event/fragment_quota_left.html #, python-format msgid "%(num)s currently available" @@ -37237,6 +37440,13 @@ msgstr "Schreibzugriff" msgid "Kosovo" msgstr "Kosovo" +#~ msgid "" +#~ "Our attempt to execute your payment via PayPal has failed. Please try " +#~ "again or contact us." +#~ msgstr "" +#~ "Unser Versuch, die Zahlung mit PayPal auszuführen, ist gescheitert. Bitte " +#~ "probieren Sie es erneut oder kontaktieren Sie uns." + #~ msgid "All actions" #~ msgstr "Alle Aktionen" @@ -37386,9 +37596,6 @@ msgstr "Kosovo" #~ msgid "Please do not use special characters in names." #~ msgstr "Bitte verwenden Sie keine Sonderzeichen in Namen." -#~ msgid "State" -#~ msgstr "Zustand" - #~ msgid "Program times: date and time" #~ msgstr "Programmzeiten: Datum und Uhrzeit" @@ -38116,9 +38323,6 @@ msgstr "Kosovo" #~ msgid "Badge design" #~ msgstr "Badge-Design" -#~ msgid "(Same as above)" -#~ msgstr "(wie oben)" - #~ msgid "Translation in development" #~ msgstr "Übersetzung in Arbeit" @@ -38817,10 +39021,6 @@ msgstr "Kosovo" #~ msgstr "" #~ "Wir haben Ihnen in den letzten 24 Stunden bereits eine E-Mail geschickt." -#~ msgctxt "attendee_data" -#~ msgid "Asked" -#~ msgstr "Abfragen" - #~ msgid "Underpaid" #~ msgstr "Unterzahlt" diff --git a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po index ce3fe217b..a0844d79e 100644 --- a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po +++ b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: 2026-07-30 18:31+0000\n" "Last-Translator: Raphael Michel \n" "Language-Team: German (informal) \n" "Language-Team: LANGUAGE \n" @@ -90,6 +90,10 @@ msgstr "" msgid "Hebrew" msgstr "" +#: pretix/_base_settings.py +msgid "Hungarian" +msgstr "" + #: pretix/_base_settings.py msgid "Indonesian" msgstr "" @@ -409,6 +413,10 @@ msgstr "" msgid "You cannot exchange a medium for a medium." msgstr "" +#: pretix/api/views/checkin.py +msgid "You cannot simulate a medium exchange." +msgstr "" + #: pretix/api/views/oauth.py pretix/control/logdisplay.py #, python-brace-format msgid "" @@ -837,8 +845,7 @@ msgstr "" #: pretix/control/forms/organizer.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/organizers/customer.html -#: pretix/control/views/item.py pretix/plugins/badges/exporters.py -#: pretix/plugins/checkinlists/exporters.py +#: pretix/plugins/badges/exporters.py pretix/plugins/checkinlists/exporters.py #: pretix/plugins/ticketoutputpdf/exporters.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html #: pretix/presale/templates/pretixpresale/event/fragment_change_form.html @@ -855,7 +862,6 @@ msgstr "" #: pretix/base/forms/questions.py pretix/base/models/customers.py #: pretix/base/models/orders.py pretix/base/pdf.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "Attendee email" msgstr "" @@ -1586,9 +1592,8 @@ msgid "Invoice sender:" msgstr "" #: pretix/base/exporters/invoices.py pretix/base/exporters/orderlist.py -#: pretix/base/forms/questions.py pretix/base/modelimport_orders.py -#: pretix/base/models/customers.py pretix/base/models/orders.py -#: pretix/control/forms/filter.py +#: pretix/base/modelimport_orders.py pretix/base/models/customers.py +#: pretix/base/models/orders.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html @@ -1600,8 +1605,7 @@ msgstr "" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "ZIP code" msgstr "" @@ -1609,8 +1613,7 @@ msgstr "" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "City" msgstr "" @@ -1633,8 +1636,7 @@ msgstr "" #: pretix/base/settings.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/email/login_notice.txt #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Country" @@ -1662,8 +1664,7 @@ msgstr "" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Company" @@ -2450,7 +2451,8 @@ msgstr "" msgid "Refund states" msgstr "" -#: pretix/base/exporters/orderlist.py pretix/base/models/orders.py +#: pretix/base/exporters/orderlist.py pretix/base/models/items.py +#: pretix/base/models/orders.py #: pretix/control/templates/pretixcontrol/datasync/failed_jobs.html #: pretix/control/templates/pretixcontrol/organizers/customer.html #: pretix/control/templates/pretixcontrol/organizers/outgoing_mail.html @@ -2955,13 +2957,11 @@ msgid "" msgstr "" #: pretix/base/forms/questions.py -msgid "" -"If you keep this empty, the ticket will be valid starting at the time of " -"purchase." +msgid "Street" msgstr "" #: pretix/base/forms/questions.py -msgid "Street and Number" +msgid "State" msgstr "" #: pretix/base/forms/questions.py @@ -2994,6 +2994,16 @@ msgstr "" msgid "Please enter a date and time no later than {max}." msgstr "" +#: pretix/base/forms/questions.py +msgid "" +"If you keep this empty, the ticket will be valid starting at the time of " +"purchase." +msgstr "" + +#: pretix/base/forms/questions.py +msgid "Street and Number" +msgstr "" + #: pretix/base/forms/questions.py msgid "" "Optional, but depending on the country you reside in we might need to charge " @@ -5320,6 +5330,10 @@ msgstr "" msgid "The count needs to be equal to or greater than zero." msgstr "" +#: pretix/base/models/items.py pretix/base/models/orders.py +msgid "Order position" +msgstr "" + #: pretix/base/models/items.py msgid "Number" msgstr "" @@ -5357,6 +5371,10 @@ msgstr "" msgid "Country code (ISO 3166-1 alpha-2)" msgstr "" +#: pretix/base/models/items.py +msgid "Asked on" +msgstr "" + #: pretix/base/models/items.py pretix/base/models/organizer.py msgid "" "The identifier may only contain letters, numbers, dots, dashes, and " @@ -5956,10 +5974,6 @@ msgstr "" msgid "Value" msgstr "" -#: pretix/base/models/orders.py -msgid "Order position" -msgstr "" - #: pretix/base/models/orders.py msgid "Cart ID (e.g. session key)" msgstr "" @@ -6868,7 +6882,7 @@ msgstr "" #: pretix/base/payment.py msgid "" -"This payment is already being processed and can not be canceled any more." +"This payment is already being processed and cannot be canceled any more." msgstr "" #: pretix/base/payment.py @@ -7595,6 +7609,14 @@ msgstr "" msgid "Presale end" msgstr "" +#: pretix/base/reldate.py +msgid "Order creation" +msgstr "" + +#: pretix/base/reldate.py +msgid "Order expiry" +msgstr "" + #: pretix/base/reldate.py msgid "before" msgstr "" @@ -7619,6 +7641,36 @@ msgstr "" msgid "Not set" msgstr "" +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'before' for '{}'" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'after' for '{}'" +msgstr "" + #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" msgstr "" @@ -8704,7 +8756,7 @@ msgstr "" msgid "Please transfer money to this bank account: 9999-9999-9999-9999" msgstr "" -#: pretix/base/services/placeholders.py pretix/control/views/organizer.py +#: pretix/base/services/placeholders.py msgid "This value will be replaced based on dynamic parameters." msgstr "" @@ -9285,6 +9337,25 @@ msgid "" "to get a ticket, such as a box office." msgstr "" +#: pretix/base/settings.py +msgid "No dates match your criteria." +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "Text for empty date results" +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "" +"This text will be shown if the calendar or list of dates is empty, e.g. " +"because a month does not contain any dates or a filter chosen by the user " +"does not find any results. You can use this to advertise ways to get in " +"touch with you to arrange further dates. We do not recommend more than one " +"or two sentences." +msgstr "" + #: pretix/base/settings.py msgid "Guidance text" msgstr "" @@ -12426,6 +12497,22 @@ msgid "" "card is issued in." msgstr "" +#: pretix/control/forms/event.py +msgid "Payment term" +msgstr "" + +#: pretix/control/forms/event.py +msgid "same as above" +msgstr "" + +#: pretix/control/forms/event.py +msgid "different payment term in days" +msgstr "" + +#: pretix/control/forms/event.py +msgid "different payment term in minutes" +msgstr "" + #: pretix/control/forms/event.py msgid "Prices including tax" msgstr "" @@ -18576,6 +18663,7 @@ msgid "Top recommendation" msgstr "" #: pretix/control/templates/pretixcontrol/event/plugins.html +#: pretix/control/templates/pretixcontrol/items/questions.html #: pretix/control/templates/pretixcontrol/organizers/plugins.html msgid "Experimental feature" msgstr "" @@ -20227,6 +20315,24 @@ msgid "" "dietary requirements." msgstr "" +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "Create a new per-ticket question" +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "Create a new order-level question" +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "Per-ticket questions" +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"These questions are asked for every ticket, so possibly multiple times in " +"the same order." +msgstr "" + #: pretix/control/templates/pretixcontrol/items/questions.html msgid "Create a new question" msgstr "" @@ -20243,6 +20349,26 @@ msgstr "" msgid "All personalized products" msgstr "" +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "Per-order questions" +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"This functionality is in active development and expected to change " +"significantly over the coming months." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"Per-order questions are currently not supported and will not be displayed in " +"pretixPOS." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "These questions are asked once per order." +msgstr "" + #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html #, python-format @@ -20765,6 +20891,13 @@ msgstr "" msgid "(optional)" msgstr "" +#: pretix/control/templates/pretixcontrol/order/change_questions.html +#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html +#: pretix/presale/templates/pretixpresale/event/checkout_questions.html +#: pretix/presale/templates/pretixpresale/event/order_modify.html +msgid "Additional order information" +msgstr "" + #: pretix/control/templates/pretixcontrol/order/delete.html msgid "Delete order" msgstr "" @@ -20792,6 +20925,23 @@ msgstr "" msgid "Extend payment term" msgstr "" +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "" +"This file has been uploaded by a user and could contain viruses or other " +"malicious content." +msgstr "" + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "UNSAFE" +msgstr "" + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +#: pretix/control/templates/pretixcontrol/order/index.html +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "not answered" +msgstr "" + #: pretix/control/templates/pretixcontrol/order/index.html #, python-format msgid "Order details: %(code)s" @@ -21047,25 +21197,10 @@ msgstr "" msgid "Ticket page" msgstr "" -#: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "not answered" -msgstr "" - #: pretix/control/templates/pretixcontrol/order/index.html msgid "This question will be asked during check-in." msgstr "" -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "" -"This file has been uploaded by a user and could contain viruses or other " -"malicious content." -msgstr "" - -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "UNSAFE" -msgstr "" - #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/orders/overview.html #: pretix/plugins/reports/accountingreport.py @@ -22303,6 +22438,15 @@ msgstr "" msgid "Hardware model" msgstr "" +#: pretix/control/templates/pretixcontrol/organizers/devices.html +#, python-format +msgid "Last seen: %(time)s" +msgstr "" + +#: pretix/control/templates/pretixcontrol/organizers/devices.html +msgid "No recent contact" +msgstr "" + #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "Not yet initialized" msgstr "" @@ -25178,10 +25322,6 @@ msgstr "" msgid "Not all objects have been selected." msgstr "" -#: pretix/control/views/item.py -msgid "Street" -msgstr "" - #: pretix/control/views/item.py msgid "The requested question does not exist." msgstr "" @@ -25327,7 +25467,7 @@ msgstr "" #: pretix/control/views/mailsetup.py msgid "" -"We did not find DMARC record for your domain. This means that there is a " +"We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" @@ -25717,7 +25857,8 @@ msgstr "" msgid "Your message has been queued and will be sent to {}." msgstr "" -#: pretix/control/views/orders.py pretix/presale/views/order.py +#: pretix/control/views/orders.py pretix/presale/views/cart.py +#: pretix/presale/views/order.py msgid "" "This link is no longer valid. Please go back, refresh the page, and try " "again." @@ -27759,6 +27900,29 @@ msgid "" "getting your money right away." msgstr "" +#: pretix/plugins/paypal2/payment.py +msgid "Allow further payments during compliance hold" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"PayPals fraud prevention might block processing of individual payments for a " +"considerable amount of time. The payment is marked as \"pending\" during " +"this time window. You can allow your customers to start another payment " +"attempts during that window. This might result in them being charged twice " +"if theoriginal payment is approved." +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "Timeout further payment attempts" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"Time duration in minutes after which another payment attempt is possible, " +"while the last payment is still under investigation." +msgstr "" + #: pretix/plugins/paypal2/payment.py msgid "-- Automatic --" msgstr "" @@ -27984,8 +28148,11 @@ msgstr "" #: pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html msgid "" -"Our attempt to execute your payment via PayPal has failed. Please try again " -"or contact us." +"Your payment is being processed by PayPal. This takes longer than usual. You " +"can wait until PayPal acknowledges the payment or you can try paying again " +"with this or another payment method. This might result in you being charged " +"twice in case PayPal allows your initial payment attempt. Please contact us " +"to resolve this case." msgstr "" #: pretix/plugins/paypal2/views.py @@ -30718,10 +30885,6 @@ msgstr[1] "" msgid "No attendee name provided" msgstr "" -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "The image you previously uploaded" -msgstr "" - #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "The price of this product was reduced because of an automatic discount." msgstr "" @@ -31119,6 +31282,10 @@ msgstr "" msgid "Set price in %(currency)s for %(item)s" msgstr "" +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "The image you previously uploaded" +msgstr "" + #: pretix/presale/templates/pretixpresale/event/fragment_quota_left.html #, python-format msgid "%(num)s currently available" diff --git a/src/pretix/locale/djangojs.pot b/src/pretix/locale/djangojs.pot index 3701bc891..fcf88cf7d 100644 --- a/src/pretix/locale/djangojs.pot +++ b/src/pretix/locale/djangojs.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/src/pretix/locale/el/LC_MESSAGES/django.po b/src/pretix/locale/el/LC_MESSAGES/django.po index 7e9a45a3f..0a762a0a1 100644 --- a/src/pretix/locale/el/LC_MESSAGES/django.po +++ b/src/pretix/locale/el/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: 2026-02-25 23:00+0000\n" "Last-Translator: David Ibáñez Cerdeira \n" "Language-Team: Greek \n" "Language-Team: Spanish \n" "Language-Team: Spanish (Latin America) \n" "Language-Team: Estonian \n" -"Language-Team: Basque \n" +"Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -91,6 +91,10 @@ msgstr "Greziera" msgid "Hebrew" msgstr "" +#: pretix/_base_settings.py +msgid "Hungarian" +msgstr "" + #: pretix/_base_settings.py msgid "Indonesian" msgstr "Indonesiera" @@ -441,6 +445,12 @@ msgstr "Bitartekaria beste ekitaldi batera konektatuta dago" msgid "You cannot exchange a medium for a medium." msgstr "Ezin duzu eskaera hau aldatu." +#: pretix/api/views/checkin.py +#, fuzzy +#| msgid "You cannot change this order." +msgid "You cannot simulate a medium exchange." +msgstr "Ezin duzu eskaera hau aldatu." + #: pretix/api/views/oauth.py pretix/control/logdisplay.py #, python-brace-format msgid "" @@ -908,8 +918,7 @@ msgstr "Zure kontuaren informazioa eguneratu" #: pretix/control/forms/organizer.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/organizers/customer.html -#: pretix/control/views/item.py pretix/plugins/badges/exporters.py -#: pretix/plugins/checkinlists/exporters.py +#: pretix/plugins/badges/exporters.py pretix/plugins/checkinlists/exporters.py #: pretix/plugins/ticketoutputpdf/exporters.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html #: pretix/presale/templates/pretixpresale/event/fragment_change_form.html @@ -928,7 +937,6 @@ msgstr "Bertaratuko den pertsonaren izena" #: pretix/base/forms/questions.py pretix/base/models/customers.py #: pretix/base/models/orders.py pretix/base/pdf.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "Attendee email" msgstr "Bertaratuko den pertsonaren e-maila" @@ -1722,9 +1730,8 @@ msgid "Invoice sender:" msgstr "Faktura igorlea:" #: pretix/base/exporters/invoices.py pretix/base/exporters/orderlist.py -#: pretix/base/forms/questions.py pretix/base/modelimport_orders.py -#: pretix/base/models/customers.py pretix/base/models/orders.py -#: pretix/control/forms/filter.py +#: pretix/base/modelimport_orders.py pretix/base/models/customers.py +#: pretix/base/models/orders.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html @@ -1736,8 +1743,7 @@ msgstr "Helbidea" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "ZIP code" msgstr "Posta-kodea" @@ -1745,8 +1751,7 @@ msgstr "Posta-kodea" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "City" msgstr "Herria" @@ -1769,8 +1774,7 @@ msgstr "Herrialdea" #: pretix/base/settings.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/email/login_notice.txt #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Country" @@ -1798,8 +1802,7 @@ msgstr "Faktura hartzailea:" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Company" @@ -2615,7 +2618,8 @@ msgstr "Ordainketen egoerak" msgid "Refund states" msgstr "Itzulketen egoerak" -#: pretix/base/exporters/orderlist.py pretix/base/models/orders.py +#: pretix/base/exporters/orderlist.py pretix/base/models/items.py +#: pretix/base/models/orders.py #: pretix/control/templates/pretixcontrol/datasync/failed_jobs.html #: pretix/control/templates/pretixcontrol/organizers/customer.html #: pretix/control/templates/pretixcontrol/organizers/outgoing_mail.html @@ -3144,16 +3148,12 @@ msgstr "" "hondatuta dago." #: pretix/base/forms/questions.py -msgid "" -"If you keep this empty, the ticket will be valid starting at the time of " -"purchase." +msgid "Street" msgstr "" -"Atal hau hutsik lagatzen baduzu, erosketa egiten den unetik aurrera balioko " -"du sarrerak." #: pretix/base/forms/questions.py -msgid "Street and Number" -msgstr "Kalea eta Zenbakia" +msgid "State" +msgstr "Herrialdea" #: pretix/base/forms/questions.py #, fuzzy, python-brace-format @@ -3190,6 +3190,18 @@ msgstr "Mesedez, sartu baliozko salmenta-kanal bat." msgid "Please enter a date and time no later than {max}." msgstr "Mesedez sartu emaitza zuzena." +#: pretix/base/forms/questions.py +msgid "" +"If you keep this empty, the ticket will be valid starting at the time of " +"purchase." +msgstr "" +"Atal hau hutsik lagatzen baduzu, erosketa egiten den unetik aurrera balioko " +"du sarrerak." + +#: pretix/base/forms/questions.py +msgid "Street and Number" +msgstr "Kalea eta Zenbakia" + #: pretix/base/forms/questions.py msgid "" "Optional, but depending on the country you reside in we might need to charge " @@ -5842,6 +5854,10 @@ msgstr "Hautatutako bariazioa ez da artikulu honetakoa." msgid "The count needs to be equal to or greater than zero." msgstr "Zenbaketak zero edo handiagoa izan behar du." +#: pretix/base/models/items.py pretix/base/models/orders.py +msgid "Order position" +msgstr "Eskaeraren posizioa" + #: pretix/base/models/items.py msgid "Number" msgstr "Zenbakia" @@ -5879,6 +5895,10 @@ msgstr "Data eta ordua" msgid "Country code (ISO 3166-1 alpha-2)" msgstr "Herrialdearen kodea (ISO 3166-1 alpha-2)" +#: pretix/base/models/items.py +msgid "Asked on" +msgstr "" + #: pretix/base/models/items.py pretix/base/models/organizer.py msgid "" "The identifier may only contain letters, numbers, dots, dashes, and " @@ -6541,10 +6561,6 @@ msgstr "Opari-txartela" msgid "Value" msgstr "Balioa" -#: pretix/base/models/orders.py -msgid "Order position" -msgstr "Eskaeraren posizioa" - #: pretix/base/models/orders.py msgid "Cart ID (e.g. session key)" msgstr "Orgaren IDa (adibidez, saio-gakoa)" @@ -7570,7 +7586,7 @@ msgstr "Jaso dugu faktura honen ordainketa." #: pretix/base/payment.py msgid "" -"This payment is already being processed and can not be canceled any more." +"This payment is already being processed and cannot be canceled any more." msgstr "" #: pretix/base/payment.py @@ -8360,6 +8376,18 @@ msgstr "Aurresalmentaren hasiera" msgid "Presale end" msgstr "Aurresalmentaren amaiera" +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order total" +msgid "Order creation" +msgstr "Eskaerak guztira" + +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order expired" +msgid "Order expiry" +msgstr "Eskaera iraungi da" + #: pretix/base/reldate.py msgid "before" msgstr "aurrekoa" @@ -8384,6 +8412,36 @@ msgstr "Ordu erlatiboa:" msgid "Not set" msgstr "Finkatu gabe" +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'before' for '{}'" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'after' for '{}'" +msgstr "" + #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" msgstr "Ausazkoa (lehenespenez, pretix aplikazio guztiekin funtzionatzen du)" @@ -9678,7 +9736,7 @@ msgstr "Zenbatekoa zure txartelean kargatu da." msgid "Please transfer money to this bank account: 9999-9999-9999-9999" msgstr "Mesedez, transferitu dirua banku-kontu honetara 9999-9999-9999-9999" -#: pretix/base/services/placeholders.py pretix/control/views/organizer.py +#: pretix/base/services/placeholders.py msgid "This value will be replaced based on dynamic parameters." msgstr "" @@ -10407,6 +10465,25 @@ msgstr "" "ezarritako salmenta-epea amaitutakoan. Sarrera bat lortzeko beste aukera " "batzuk deskribatzeko erabil dezakezu, adibidez, leihatila bat." +#: pretix/base/settings.py +msgid "No dates match your criteria." +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "Text for empty date results" +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "" +"This text will be shown if the calendar or list of dates is empty, e.g. " +"because a month does not contain any dates or a filter chosen by the user " +"does not find any results. You can use this to advertise ways to get in " +"touch with you to arrange further dates. We do not recommend more than one " +"or two sentences." +msgstr "" + #: pretix/base/settings.py msgid "Guidance text" msgstr "Laguntza-testua" @@ -13545,8 +13622,8 @@ msgid "" "You are receiving this email because you placed an order for " "%(event)s." msgstr "" -"Mezu elektroniko hau jasotzen ari zara eskaera bat egin duzulako %" -"(event)s(e)rako." +"Mezu elektroniko hau jasotzen ari zara eskaera bat egin duzulako " +"%(event)s(e)rako." #: pretix/base/templates/pretixbase/email/order_details.html #: pretix/control/templates/pretixcontrol/organizers/customer.html @@ -14375,6 +14452,28 @@ msgid "" "card is issued in." msgstr "" +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Set payment term" +msgid "Payment term" +msgstr "Ordainketa epea finkatu" + +#: pretix/control/forms/event.py +msgid "same as above" +msgstr "" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in days" +msgid "different payment term in days" +msgstr "Ordaintzeko epea, egunetan" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in minutes" +msgid "different payment term in minutes" +msgstr "Ordaintzeko epea minututan" + #: pretix/control/forms/event.py #, fuzzy #| msgid "Price including add-ons" @@ -20691,6 +20790,7 @@ msgid "Top recommendation" msgstr "" #: pretix/control/templates/pretixcontrol/event/plugins.html +#: pretix/control/templates/pretixcontrol/items/questions.html #: pretix/control/templates/pretixcontrol/organizers/plugins.html msgid "Experimental feature" msgstr "" @@ -22367,6 +22467,30 @@ msgid "" "dietary requirements." msgstr "" +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new check-in rule" +msgid "Create a new per-ticket question" +msgstr "Izen-emate arau berri bat sortu" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new rule" +msgid "Create a new order-level question" +msgstr "Arau berria sortu" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Include questions" +msgid "Per-ticket questions" +msgstr "Galderak sartu" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"These questions are asked for every ticket, so possibly multiple times in " +"the same order." +msgstr "" + #: pretix/control/templates/pretixcontrol/items/questions.html msgid "Create a new question" msgstr "" @@ -22383,6 +22507,30 @@ msgstr "" msgid "All personalized products" msgstr "" +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Include questions" +msgid "Per-order questions" +msgstr "Galderak sartu" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"This functionality is in active development and expected to change " +"significantly over the coming months." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"Per-order questions are currently not supported and will not be displayed in " +"pretixPOS." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Require a phone number per order" +msgid "These questions are asked once per order." +msgstr "Eskatu telefono zenbakia eskaera bakoitzeko" + #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html #, python-format @@ -22912,6 +23060,15 @@ msgstr "Fakturazio datuak" msgid "(optional)" msgstr "(hautazkoa)" +#: pretix/control/templates/pretixcontrol/order/change_questions.html +#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html +#: pretix/presale/templates/pretixpresale/event/checkout_questions.html +#: pretix/presale/templates/pretixpresale/event/order_modify.html +#, fuzzy +#| msgid "Additional information" +msgid "Additional order information" +msgstr "Informazio gehigarria" + #: pretix/control/templates/pretixcontrol/order/delete.html msgid "Delete order" msgstr "" @@ -22939,6 +23096,23 @@ msgstr "" msgid "Extend payment term" msgstr "" +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "" +"This file has been uploaded by a user and could contain viruses or other " +"malicious content." +msgstr "" + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "UNSAFE" +msgstr "" + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +#: pretix/control/templates/pretixcontrol/order/index.html +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "not answered" +msgstr "" + #: pretix/control/templates/pretixcontrol/order/index.html #, python-format msgid "Order details: %(code)s" @@ -23211,25 +23385,10 @@ msgstr "" msgid "Ticket page" msgstr "" -#: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "not answered" -msgstr "" - #: pretix/control/templates/pretixcontrol/order/index.html msgid "This question will be asked during check-in." msgstr "" -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "" -"This file has been uploaded by a user and could contain viruses or other " -"malicious content." -msgstr "" - -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "UNSAFE" -msgstr "" - #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/orders/overview.html #: pretix/plugins/reports/accountingreport.py @@ -24482,6 +24641,16 @@ msgstr "" msgid "Hardware model" msgstr "" +#: pretix/control/templates/pretixcontrol/organizers/devices.html +#, fuzzy, python-format +#| msgid "Begin: %(time)s" +msgid "Last seen: %(time)s" +msgstr "Hasiera: %(time)s" + +#: pretix/control/templates/pretixcontrol/organizers/devices.html +msgid "No recent contact" +msgstr "" + #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "Not yet initialized" msgstr "" @@ -27430,10 +27599,6 @@ msgstr "" msgid "Not all objects have been selected." msgstr "" -#: pretix/control/views/item.py -msgid "Street" -msgstr "" - #: pretix/control/views/item.py msgid "The requested question does not exist." msgstr "" @@ -27581,7 +27746,7 @@ msgstr "" #: pretix/control/views/mailsetup.py msgid "" -"We did not find DMARC record for your domain. This means that there is a " +"We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" @@ -27977,7 +28142,8 @@ msgstr "" msgid "Your message has been queued and will be sent to {}." msgstr "" -#: pretix/control/views/orders.py pretix/presale/views/order.py +#: pretix/control/views/orders.py pretix/presale/views/cart.py +#: pretix/presale/views/order.py msgid "" "This link is no longer valid. Please go back, refresh the page, and try " "again." @@ -30249,6 +30415,29 @@ msgstr "" "epetan edo 30 egunetan ordaintzeko aukera. Zuk, saltzaile gisa, berehala " "jasotzen duzu zure dirua." +#: pretix/plugins/paypal2/payment.py +msgid "Allow further payments during compliance hold" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"PayPals fraud prevention might block processing of individual payments for a " +"considerable amount of time. The payment is marked as \"pending\" during " +"this time window. You can allow your customers to start another payment " +"attempts during that window. This might result in them being charged twice " +"if theoriginal payment is approved." +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "Timeout further payment attempts" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"Time duration in minutes after which another payment attempt is possible, " +"while the last payment is still under investigation." +msgstr "" + #: pretix/plugins/paypal2/payment.py msgid "-- Automatic --" msgstr "-- Automatikoa --" @@ -30506,11 +30695,12 @@ msgstr "Hautatu nola ordaindu nahi duzun." #: pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html msgid "" -"Our attempt to execute your payment via PayPal has failed. Please try again " -"or contact us." +"Your payment is being processed by PayPal. This takes longer than usual. You " +"can wait until PayPal acknowledges the payment or you can try paying again " +"with this or another payment method. This might result in you being charged " +"twice in case PayPal allows your initial payment attempt. Please contact us " +"to resolve this case." msgstr "" -"Ordainketa PayPal bidez egiteko gure saiakerak huts egin du. Mesedez saiatu " -"berriro edo jarri gurekin harremanetan." #: pretix/plugins/paypal2/views.py msgid "" @@ -33631,10 +33821,6 @@ msgstr[1] "Sarrera hau %(count)s aldiz erabili da." msgid "No attendee name provided" msgstr "Ez da bertaratuko den pertsonaren izena adierazi" -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "The image you previously uploaded" -msgstr "Lehenago igo duzun irudia" - #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "The price of this product was reduced because of an automatic discount." msgstr "" @@ -34076,6 +34262,10 @@ msgstr "%(value)s zergarik gabe" msgid "Set price in %(currency)s for %(item)s" msgstr "%(item)s-(e)rako %(currency)s)tan zehaztutako prezioa" +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "The image you previously uploaded" +msgstr "Lehenago igo duzun irudia" + #: pretix/presale/templates/pretixpresale/event/fragment_quota_left.html #, python-format msgid "%(num)s currently available" @@ -35858,6 +36048,13 @@ msgstr "Idatzi sarbidea" msgid "Kosovo" msgstr "Kosovo" +#~ msgid "" +#~ "Our attempt to execute your payment via PayPal has failed. Please try " +#~ "again or contact us." +#~ msgstr "" +#~ "Ordainketa PayPal bidez egiteko gure saiakerak huts egin du. Mesedez " +#~ "saiatu berriro edo jarri gurekin harremanetan." + #, python-brace-format #~ msgid "{text}" #~ msgstr "" @@ -35924,9 +36121,6 @@ msgstr "Kosovo" #~ msgid "Please do not use special characters in names." #~ msgstr "Ez erabili karaktere berezirik izenetan." -#~ msgid "State" -#~ msgstr "Herrialdea" - #, fuzzy #~| msgid "Printing date and time" #~ msgid "Program times: date and time" diff --git a/src/pretix/locale/fi/LC_MESSAGES/django.po b/src/pretix/locale/fi/LC_MESSAGES/django.po index 1311ff385..a2d47db80 100644 --- a/src/pretix/locale/fi/LC_MESSAGES/django.po +++ b/src/pretix/locale/fi/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: 2025-10-04 10:10+0000\n" "Last-Translator: Sebastian Bożek \n" "Language-Team: Finnish {text}" #~ msgstr "" @@ -35756,9 +35953,6 @@ msgstr "Kosovo" #~ msgid "Please do not use special characters in names." #~ msgstr "Älä käytä erikoismerkkejä nimissä." -#~ msgid "State" -#~ msgstr "Osavaltio/Maakunta" - #, fuzzy #~| msgid "Printing date and time" #~ msgid "Program times: date and time" diff --git a/src/pretix/locale/fo/LC_MESSAGES/django.po b/src/pretix/locale/fo/LC_MESSAGES/django.po index 8f9ced60d..13bc57f26 100644 --- a/src/pretix/locale/fo/LC_MESSAGES/django.po +++ b/src/pretix/locale/fo/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: 2025-02-13 22:00+0000\n" "Last-Translator: Andrias Magnussen \n" "Language-Team: Faroese \n" -"Language-Team: French \n" +"Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -87,6 +87,10 @@ msgstr "Grec" msgid "Hebrew" msgstr "Hébreu" +#: pretix/_base_settings.py +msgid "Hungarian" +msgstr "" + #: pretix/_base_settings.py msgid "Indonesian" msgstr "Indonésien" @@ -433,6 +437,12 @@ msgstr "Média connecté à un autre événement" msgid "You cannot exchange a medium for a medium." msgstr "Il n'est pas possible d'échanger un support contre un autre support." +#: pretix/api/views/checkin.py +#, fuzzy +#| msgid "Product does not support medium exchange." +msgid "You cannot simulate a medium exchange." +msgstr "Ce produit ne permet pas de changer de support." + #: pretix/api/views/oauth.py pretix/control/logdisplay.py #, python-brace-format msgid "" @@ -887,8 +897,7 @@ msgstr "Informations sur l'événement ou la date" #: pretix/control/forms/organizer.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/organizers/customer.html -#: pretix/control/views/item.py pretix/plugins/badges/exporters.py -#: pretix/plugins/checkinlists/exporters.py +#: pretix/plugins/badges/exporters.py pretix/plugins/checkinlists/exporters.py #: pretix/plugins/ticketoutputpdf/exporters.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html #: pretix/presale/templates/pretixpresale/event/fragment_change_form.html @@ -905,7 +914,6 @@ msgstr "Participant" #: pretix/base/forms/questions.py pretix/base/models/customers.py #: pretix/base/models/orders.py pretix/base/pdf.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "Attendee email" msgstr "E-mail du participant" @@ -1657,9 +1665,8 @@ msgid "Invoice sender:" msgstr "Expéditeur de facture :" #: pretix/base/exporters/invoices.py pretix/base/exporters/orderlist.py -#: pretix/base/forms/questions.py pretix/base/modelimport_orders.py -#: pretix/base/models/customers.py pretix/base/models/orders.py -#: pretix/control/forms/filter.py +#: pretix/base/modelimport_orders.py pretix/base/models/customers.py +#: pretix/base/models/orders.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html @@ -1671,8 +1678,7 @@ msgstr "Adresse" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "ZIP code" msgstr "Code postal" @@ -1680,8 +1686,7 @@ msgstr "Code postal" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "City" msgstr "Ville" @@ -1704,8 +1709,7 @@ msgstr "État" #: pretix/base/settings.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/email/login_notice.txt #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Country" @@ -1733,8 +1737,7 @@ msgstr "Destinataire de facture :" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Company" @@ -2551,7 +2554,8 @@ msgstr "Etats du paiement" msgid "Refund states" msgstr "Etats des remboursements" -#: pretix/base/exporters/orderlist.py pretix/base/models/orders.py +#: pretix/base/exporters/orderlist.py pretix/base/models/items.py +#: pretix/base/models/orders.py #: pretix/control/templates/pretixcontrol/datasync/failed_jobs.html #: pretix/control/templates/pretixcontrol/organizers/customer.html #: pretix/control/templates/pretixcontrol/organizers/outgoing_mail.html @@ -3081,16 +3085,12 @@ msgstr "" "pas une image, soit est une image corrompue." #: pretix/base/forms/questions.py -msgid "" -"If you keep this empty, the ticket will be valid starting at the time of " -"purchase." -msgstr "" -"Si vous laissez ce champ vide, le billet sera valable à partir du moment de " -"l'achat." +msgid "Street" +msgstr "Rue" #: pretix/base/forms/questions.py -msgid "Street and Number" -msgstr "Rue et numéro" +msgid "State" +msgstr "État" #: pretix/base/forms/questions.py #, python-brace-format @@ -3123,6 +3123,18 @@ msgstr "" msgid "Please enter a date and time no later than {max}." msgstr "Veuillez saisir une date et une heure au plus tard le {max}." +#: pretix/base/forms/questions.py +msgid "" +"If you keep this empty, the ticket will be valid starting at the time of " +"purchase." +msgstr "" +"Si vous laissez ce champ vide, le billet sera valable à partir du moment de " +"l'achat." + +#: pretix/base/forms/questions.py +msgid "Street and Number" +msgstr "Rue et numéro" + #: pretix/base/forms/questions.py msgid "" "Optional, but depending on the country you reside in we might need to charge " @@ -5778,6 +5790,10 @@ msgstr "Cette variante n'appartient pas à ce produit." msgid "The count needs to be equal to or greater than zero." msgstr "Le nombre doit être égal ou supérieur à zéro." +#: pretix/base/models/items.py pretix/base/models/orders.py +msgid "Order position" +msgstr "Position de l'ordre" + #: pretix/base/models/items.py msgid "Number" msgstr "Nombre" @@ -5815,6 +5831,10 @@ msgstr "Date et heure" msgid "Country code (ISO 3166-1 alpha-2)" msgstr "Code pays (ISO 3166-1 alpha-2)" +#: pretix/base/models/items.py +msgid "Asked on" +msgstr "" + #: pretix/base/models/items.py pretix/base/models/organizer.py msgid "" "The identifier may only contain letters, numbers, dots, dashes, and " @@ -6483,10 +6503,6 @@ msgstr "Carte-cadeau" msgid "Value" msgstr "Valeur" -#: pretix/base/models/orders.py -msgid "Order position" -msgstr "Position de l'ordre" - #: pretix/base/models/orders.py msgid "Cart ID (e.g. session key)" msgstr "ID du panier (par ex. clé de session)" @@ -7539,8 +7555,11 @@ msgid "The payment for this invoice has already been received." msgstr "Le paiement de cette facture a déjà été reçu." #: pretix/base/payment.py +#, fuzzy +#| msgid "" +#| "This payment is already being processed and can not be canceled any more." msgid "" -"This payment is already being processed and can not be canceled any more." +"This payment is already being processed and cannot be canceled any more." msgstr "" "Ce paiement est déjà en cours de traitement et ne peut plus être annulé." @@ -8320,6 +8339,18 @@ msgstr "Début de la prévente" msgid "Presale end" msgstr "Fin de la prévente" +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order email" +msgid "Order creation" +msgstr "E-mail de la commande" + +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order expired" +msgid "Order expiry" +msgstr "Commande expirée" + #: pretix/base/reldate.py msgid "before" msgstr "avant" @@ -8344,6 +8375,36 @@ msgstr "Heure relative :" msgid "Not set" msgstr "Non réglé" +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'before' for '{}'" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'after' for '{}'" +msgstr "" + #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" msgstr "Aléatoire (par défaut, fonctionne avec toutes les applications pretix)" @@ -9644,7 +9705,7 @@ msgid "Please transfer money to this bank account: 9999-9999-9999-9999" msgstr "" "Veuillez virer de l'argent sur ce compte bancaire : 9999-9999-9999-9999" -#: pretix/base/services/placeholders.py pretix/control/views/organizer.py +#: pretix/base/services/placeholders.py msgid "This value will be replaced based on dynamic parameters." msgstr "Cette valeur sera remplacée en fonction des paramètres dynamiques." @@ -10377,6 +10438,25 @@ msgstr "" "désigné pour cet événement terminé. Vous pouvez l’utiliser pour décrire " "d’autres options pour obtenir un billet, comme une billetterie." +#: pretix/base/settings.py +msgid "No dates match your criteria." +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "Text for empty date results" +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "" +"This text will be shown if the calendar or list of dates is empty, e.g. " +"because a month does not contain any dates or a filter chosen by the user " +"does not find any results. You can use this to advertise ways to get in " +"touch with you to arrange further dates. We do not recommend more than one " +"or two sentences." +msgstr "" + #: pretix/base/settings.py msgid "Guidance text" msgstr "Texte d’aide" @@ -14408,6 +14488,30 @@ msgstr "" "Vous avez configuré les cartes cadeaux pour qu’elles soient valides {} ans " "plus l’année d’émission de la carte-cadeau." +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment date" +msgid "Payment term" +msgstr "Date de paiement" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "(Same as above)" +msgid "same as above" +msgstr "(identique à ce qui précède)" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in days" +msgid "different payment term in days" +msgstr "Délai de paiement en jours" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in minutes" +msgid "different payment term in minutes" +msgstr "Délai de paiement en minutes" + #: pretix/control/forms/event.py msgid "Prices including tax" msgstr "Prix TTC" @@ -21372,6 +21476,7 @@ msgid "Top recommendation" msgstr "Recommandation principale" #: pretix/control/templates/pretixcontrol/event/plugins.html +#: pretix/control/templates/pretixcontrol/items/questions.html #: pretix/control/templates/pretixcontrol/organizers/plugins.html msgid "Experimental feature" msgstr "Fonctionalité expérimentale" @@ -23311,6 +23416,30 @@ msgstr "" "exemple pourrait être de demander à vos utilisateurs quels sont leurs " "besoins alimentaires." +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new question" +msgid "Create a new per-ticket question" +msgstr "Créer une nouvelle question" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new question" +msgid "Create a new order-level question" +msgstr "Créer une nouvelle question" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Delete question" +msgid "Per-ticket questions" +msgstr "Supprimer la question" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"These questions are asked for every ticket, so possibly multiple times in " +"the same order." +msgstr "" + #: pretix/control/templates/pretixcontrol/items/questions.html msgid "Create a new question" msgstr "Créer une nouvelle question" @@ -23327,6 +23456,30 @@ msgstr "Demander lors de l’enregistrement" msgid "All personalized products" msgstr "Tous les produits personnalisés" +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Include questions" +msgid "Per-order questions" +msgstr "Inclure des questions" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"This functionality is in active development and expected to change " +"significantly over the coming months." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"Per-order questions are currently not supported and will not be displayed in " +"pretixPOS." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "The question has been reordered." +msgid "These questions are asked once per order." +msgstr "La question a été réordonnée." + #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html #, python-format @@ -23933,6 +24086,15 @@ msgstr "Facturation" msgid "(optional)" msgstr "(optionnel)" +#: pretix/control/templates/pretixcontrol/order/change_questions.html +#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html +#: pretix/presale/templates/pretixpresale/event/checkout_questions.html +#: pretix/presale/templates/pretixpresale/event/order_modify.html +#, fuzzy +#| msgid "Additional information" +msgid "Additional order information" +msgstr "Informations complémentaires" + #: pretix/control/templates/pretixcontrol/order/delete.html msgid "Delete order" msgstr "Supprimer la commande" @@ -23962,6 +24124,25 @@ msgstr "Oui, refuser la commande" msgid "Extend payment term" msgstr "Prolonger le délai de paiement" +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "" +"This file has been uploaded by a user and could contain viruses or other " +"malicious content." +msgstr "" +"Ce fichier a été chargé par un utilisateur et peut contenir des virus ou " +"tout autre contenu malveillant." + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "UNSAFE" +msgstr "PEU SÛR" + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +#: pretix/control/templates/pretixcontrol/order/index.html +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "not answered" +msgstr "pas de réponse" + #: pretix/control/templates/pretixcontrol/order/index.html #, python-format msgid "Order details: %(code)s" @@ -24244,27 +24425,10 @@ msgstr "%(datetime_range)s valides" msgid "Ticket page" msgstr "Page de ticket" -#: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "not answered" -msgstr "pas de réponse" - #: pretix/control/templates/pretixcontrol/order/index.html msgid "This question will be asked during check-in." msgstr "Cette question sera posée lors de l'enregistrement." -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "" -"This file has been uploaded by a user and could contain viruses or other " -"malicious content." -msgstr "" -"Ce fichier a été chargé par un utilisateur et peut contenir des virus ou " -"tout autre contenu malveillant." - -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "UNSAFE" -msgstr "PEU SÛR" - #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/orders/overview.html #: pretix/plugins/reports/accountingreport.py @@ -25644,6 +25808,16 @@ msgstr "Connecter un appareil" msgid "Hardware model" msgstr "Modèle de matériel" +#: pretix/control/templates/pretixcontrol/organizers/devices.html +#, fuzzy, python-format +#| msgid "Begin: %(time)s" +msgid "Last seen: %(time)s" +msgstr "Début : %(time)s" + +#: pretix/control/templates/pretixcontrol/organizers/devices.html +msgid "No recent contact" +msgstr "" + #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "Not yet initialized" msgstr "Pas encore initialisé" @@ -28972,10 +29146,6 @@ msgstr "Le classement des catégories a été mis à jour." msgid "Not all objects have been selected." msgstr "Tous les objets n’ont pas été sélectionnés." -#: pretix/control/views/item.py -msgid "Street" -msgstr "Rue" - #: pretix/control/views/item.py msgid "The requested question does not exist." msgstr "La question demandée n'existe pas." @@ -29177,7 +29347,7 @@ msgstr "" #| "You should update the DNS settings of your domain to include this system " #| "in the SPF record." msgid "" -"We did not find DMARC record for your domain. This means that there is a " +"We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" @@ -29643,7 +29813,8 @@ msgstr "Sujet : {subject}" msgid "Your message has been queued and will be sent to {}." msgstr "Votre message a été mis en file d'attente et sera envoyé à {}." -#: pretix/control/views/orders.py pretix/presale/views/order.py +#: pretix/control/views/orders.py pretix/presale/views/cart.py +#: pretix/presale/views/order.py msgid "" "This link is no longer valid. Please go back, refresh the page, and try " "again." @@ -31967,6 +32138,29 @@ msgstr "" "certaine limite) et de payer en plusieurs fois ou dans les 30 jours. Vous, " "en tant que commerçant, recevez votre argent tout de suite." +#: pretix/plugins/paypal2/payment.py +msgid "Allow further payments during compliance hold" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"PayPals fraud prevention might block processing of individual payments for a " +"considerable amount of time. The payment is marked as \"pending\" during " +"this time window. You can allow your customers to start another payment " +"attempts during that window. This might result in them being charged twice " +"if theoriginal payment is approved." +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "Timeout further payment attempts" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"Time duration in minutes after which another payment attempt is possible, " +"while the last payment is still under investigation." +msgstr "" + #: pretix/plugins/paypal2/payment.py msgid "-- Automatic --" msgstr "-- Automatique --" @@ -32237,11 +32431,12 @@ msgstr "Veuillez choisir comment vous voulez payer." #: pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html msgid "" -"Our attempt to execute your payment via PayPal has failed. Please try again " -"or contact us." +"Your payment is being processed by PayPal. This takes longer than usual. You " +"can wait until PayPal acknowledges the payment or you can try paying again " +"with this or another payment method. This might result in you being charged " +"twice in case PayPal allows your initial payment attempt. Please contact us " +"to resolve this case." msgstr "" -"Notre tentative d'exécution de votre paiement via PayPal a échoué. Veuillez " -"réessayer ou nous contacter." #: pretix/plugins/paypal2/views.py msgid "" @@ -35333,10 +35528,6 @@ msgstr[1] "Ce billet a été utilisé %(count)s fois." msgid "No attendee name provided" msgstr "Aucun nom de participant fourni" -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "The image you previously uploaded" -msgstr "L'image que vous avez précédemment téléchargée" - #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "The price of this product was reduced because of an automatic discount." msgstr "Le prix de ce produit a été réduit en raison d’une remise automatique." @@ -35774,6 +35965,10 @@ msgstr "%(value)s hors taxes" msgid "Set price in %(currency)s for %(item)s" msgstr "Définir le prix en %(currency)s pour %(item)s" +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "The image you previously uploaded" +msgstr "L'image que vous avez précédemment téléchargée" + #: pretix/presale/templates/pretixpresale/event/fragment_quota_left.html #, python-format msgid "%(num)s currently available" @@ -37593,6 +37788,13 @@ msgstr "Accès en écriture" msgid "Kosovo" msgstr "Kosovo" +#~ msgid "" +#~ "Our attempt to execute your payment via PayPal has failed. Please try " +#~ "again or contact us." +#~ msgstr "" +#~ "Notre tentative d'exécution de votre paiement via PayPal a échoué. " +#~ "Veuillez réessayer ou nous contacter." + #~ msgid "All actions" #~ msgstr "Toutes les actions" @@ -37733,9 +37935,6 @@ msgstr "Kosovo" #~ msgid "Please do not use special characters in names." #~ msgstr "Merci de ne pas utiliser de caractères spéciaux dans les noms." -#~ msgid "State" -#~ msgstr "État" - #~ msgid "Program times: date and time" #~ msgstr "Horaires du programme : date et heure" @@ -38468,9 +38667,6 @@ msgstr "Kosovo" #~ msgid "Badge design" #~ msgstr "Conception du badge" -#~ msgid "(Same as above)" -#~ msgstr "(identique à ce qui précède)" - #~ msgid "Translation in development" #~ msgstr "Traduction en développement" diff --git a/src/pretix/locale/gl/LC_MESSAGES/django.po b/src/pretix/locale/gl/LC_MESSAGES/django.po index 326e6cb86..edf49214c 100644 --- a/src/pretix/locale/gl/LC_MESSAGES/django.po +++ b/src/pretix/locale/gl/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: 2026-04-21 11:05+0000\n" "Last-Translator: Sandra Rial Pérez \n" "Language-Team: Galician \n" "Language-Team: Hebrew \n" "Language-Team: Croatian \n" "Language-Team: Hungarian \n" "Language-Team: Indonesian \n" @@ -92,6 +92,10 @@ msgstr "Greco" msgid "Hebrew" msgstr "Ebraico" +#: pretix/_base_settings.py +msgid "Hungarian" +msgstr "" + #: pretix/_base_settings.py msgid "Indonesian" msgstr "Indonesiano" @@ -438,6 +442,12 @@ msgstr "Mezzo connesso a un altro evento" msgid "You cannot exchange a medium for a medium." msgstr "Non puoi scambiare un mezzo per un mezzo." +#: pretix/api/views/checkin.py +#, fuzzy +#| msgid "Product does not support medium exchange." +msgid "You cannot simulate a medium exchange." +msgstr "Il prodotto non supporta lo scambio di supporto." + #: pretix/api/views/oauth.py pretix/control/logdisplay.py #, python-brace-format msgid "" @@ -888,8 +898,7 @@ msgstr "Informazioni sull'evento o sulla data" #: pretix/control/forms/organizer.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/organizers/customer.html -#: pretix/control/views/item.py pretix/plugins/badges/exporters.py -#: pretix/plugins/checkinlists/exporters.py +#: pretix/plugins/badges/exporters.py pretix/plugins/checkinlists/exporters.py #: pretix/plugins/ticketoutputpdf/exporters.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html #: pretix/presale/templates/pretixpresale/event/fragment_change_form.html @@ -906,7 +915,6 @@ msgstr "Partecipante" #: pretix/base/forms/questions.py pretix/base/models/customers.py #: pretix/base/models/orders.py pretix/base/pdf.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "Attendee email" msgstr "Email partecipante" @@ -1654,9 +1662,8 @@ msgid "Invoice sender:" msgstr "Mittente fattura:" #: pretix/base/exporters/invoices.py pretix/base/exporters/orderlist.py -#: pretix/base/forms/questions.py pretix/base/modelimport_orders.py -#: pretix/base/models/customers.py pretix/base/models/orders.py -#: pretix/control/forms/filter.py +#: pretix/base/modelimport_orders.py pretix/base/models/customers.py +#: pretix/base/models/orders.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html @@ -1668,8 +1675,7 @@ msgstr "Indirizzo" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "ZIP code" msgstr "CAP" @@ -1677,8 +1683,7 @@ msgstr "CAP" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "City" msgstr "Città" @@ -1701,8 +1706,7 @@ msgstr "Stato" #: pretix/base/settings.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/email/login_notice.txt #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Country" @@ -1730,8 +1734,7 @@ msgstr "Destinatario fattura:" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Company" @@ -2547,7 +2550,8 @@ msgstr "Stati di pagamento" msgid "Refund states" msgstr "Stato rimborsi" -#: pretix/base/exporters/orderlist.py pretix/base/models/orders.py +#: pretix/base/exporters/orderlist.py pretix/base/models/items.py +#: pretix/base/models/orders.py #: pretix/control/templates/pretixcontrol/datasync/failed_jobs.html #: pretix/control/templates/pretixcontrol/organizers/customer.html #: pretix/control/templates/pretixcontrol/organizers/outgoing_mail.html @@ -3070,15 +3074,13 @@ msgstr "" "non è un'immagine." #: pretix/base/forms/questions.py -msgid "" -"If you keep this empty, the ticket will be valid starting at the time of " -"purchase." -msgstr "" -"Se lo lasci vuoto, il ticket sarà valido a partire dalla data di vendita." +#, fuzzy +msgid "Street" +msgstr "Indirizzo" #: pretix/base/forms/questions.py -msgid "Street and Number" -msgstr "Indirizzo e numero civico" +msgid "State" +msgstr "Stato" #: pretix/base/forms/questions.py #, python-brace-format @@ -3110,6 +3112,17 @@ msgstr "Per favore inserisci data e orario successivi a {min}." msgid "Please enter a date and time no later than {max}." msgstr "Per favore inserisci data e orario precedenti a {max}." +#: pretix/base/forms/questions.py +msgid "" +"If you keep this empty, the ticket will be valid starting at the time of " +"purchase." +msgstr "" +"Se lo lasci vuoto, il ticket sarà valido a partire dalla data di vendita." + +#: pretix/base/forms/questions.py +msgid "Street and Number" +msgstr "Indirizzo e numero civico" + #: pretix/base/forms/questions.py msgid "" "Optional, but depending on the country you reside in we might need to charge " @@ -3977,8 +3990,8 @@ msgstr "Variazione di prezzo" #, python-brace-format msgid "Could not parse {value} as a price effect, use one of {options}." msgstr "" -"Non è possible leggere {value} come un effetto prezzo, usare una di {options}" -"." +"Non è possible leggere {value} come un effetto prezzo, usare una di " +"{options}." #: pretix/base/modelimport_vouchers.py pretix/base/models/vouchers.py msgid "Voucher value" @@ -5745,6 +5758,10 @@ msgstr "La variante scelta non appartiene a questo prodotto." msgid "The count needs to be equal to or greater than zero." msgstr "Il conteggio deve essere uguale o superiore a zero." +#: pretix/base/models/items.py pretix/base/models/orders.py +msgid "Order position" +msgstr "Posizione dell'ordine" + #: pretix/base/models/items.py msgid "Number" msgstr "Numero" @@ -5782,6 +5799,10 @@ msgstr "Data e ora" msgid "Country code (ISO 3166-1 alpha-2)" msgstr "Codice nazione (ISO 3166-1 alpha-2)" +#: pretix/base/models/items.py +msgid "Asked on" +msgstr "" + #: pretix/base/models/items.py pretix/base/models/organizer.py msgid "" "The identifier may only contain letters, numbers, dots, dashes, and " @@ -6446,10 +6467,6 @@ msgstr "Carta regalo" msgid "Value" msgstr "Valore" -#: pretix/base/models/orders.py -msgid "Order position" -msgstr "Posizione dell'ordine" - #: pretix/base/models/orders.py msgid "Cart ID (e.g. session key)" msgstr "ID carrello (p.es. chiave di sessione)" @@ -7493,8 +7510,11 @@ msgid "The payment for this invoice has already been received." msgstr "Il pagamento per questa fattura è già stato ricevuto." #: pretix/base/payment.py +#, fuzzy +#| msgid "" +#| "This payment is already being processed and can not be canceled any more." msgid "" -"This payment is already being processed and can not be canceled any more." +"This payment is already being processed and cannot be canceled any more." msgstr "" "Questo pagamento è già in fase di elaborazione e non può più essere " "annullato." @@ -8266,6 +8286,18 @@ msgstr "Inizio prevendita" msgid "Presale end" msgstr "Fine prevendita" +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order email" +msgid "Order creation" +msgstr "Indirizzo email dell'ordine" + +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order expired" +msgid "Order expiry" +msgstr "Ordine scaduto" + #: pretix/base/reldate.py msgid "before" msgstr "prima di" @@ -8290,6 +8322,36 @@ msgstr "Ora relativa:" msgid "Not set" msgstr "Non impostato" +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'before' for '{}'" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'after' for '{}'" +msgstr "" + #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" msgstr "Casuale (predefinito, funziona con tutte le app pretix)" @@ -8475,11 +8537,11 @@ msgid_plural "" "The voucher code \"%(voucher)s\" can only be used if you select at least " "%(number)s matching products." msgstr[0] "" -"Il codice buono \"%(voucher)s\" può essere utilizzato se selezioni almeno %" -"(number)s prodotto corrispondente." +"Il codice buono \"%(voucher)s\" può essere utilizzato se selezioni almeno " +"%(number)s prodotto corrispondente." msgstr[1] "" -"Il codice buono \"%(voucher)s\" può essere utilizzato se selezioni almeno %" -"(number)s prodotti corrispondenti." +"Il codice buono \"%(voucher)s\" può essere utilizzato se selezioni almeno " +"%(number)s prodotti corrispondenti." #: pretix/base/services/cart.py #, python-format @@ -8492,13 +8554,15 @@ msgid_plural "" "%(number)s matching products. We have therefore removed some positions from " "your cart that can no longer be purchased like this." msgstr[0] "" -"Il codice buono \"%(voucher)s\" può essere utilizzato se selezioni almeno %" -"(number)s prodotto corrispondente. Abbiamo pertanto rimosso dal tuo carrello " -"alcune posizioni che non possono più essere acquistate in queste modalità." +"Il codice buono \"%(voucher)s\" può essere utilizzato se selezioni almeno " +"%(number)s prodotto corrispondente. Abbiamo pertanto rimosso dal tuo " +"carrello alcune posizioni che non possono più essere acquistate in queste " +"modalità." msgstr[1] "" -"Il codice buono \"%(voucher)s\" può essere utilizzato se selezioni almeno %" -"(number)s prodotti corrispondenti. Abbiamo pertanto rimosso dal tuo carrello " -"alcune posizioni che non possono più essere acquistate in queste modalità." +"Il codice buono \"%(voucher)s\" può essere utilizzato se selezioni almeno " +"%(number)s prodotti corrispondenti. Abbiamo pertanto rimosso dal tuo " +"carrello alcune posizioni che non possono più essere acquistate in queste " +"modalità." #: pretix/base/services/cart.py msgid "" @@ -9579,7 +9643,7 @@ msgid "Please transfer money to this bank account: 9999-9999-9999-9999" msgstr "" "Per favore, trasferisci i soldi a questo conto bancario: 9999-9999-9999-9999" -#: pretix/base/services/placeholders.py pretix/control/views/organizer.py +#: pretix/base/services/placeholders.py msgid "This value will be replaced based on dynamic parameters." msgstr "Questo valore verrà sostituito in base a parametri dinamici." @@ -10303,6 +10367,25 @@ msgstr "" "vendita dei biglietti viene raggiunto. Puoi utilizzarlo per descrivere " "alternative per l'acquisto dei biglietti, come ad esempio un box office." +#: pretix/base/settings.py +msgid "No dates match your criteria." +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "Text for empty date results" +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "" +"This text will be shown if the calendar or list of dates is empty, e.g. " +"because a month does not contain any dates or a filter chosen by the user " +"does not find any results. You can use this to advertise ways to get in " +"touch with you to arrange further dates. We do not recommend more than one " +"or two sentences." +msgstr "" + #: pretix/base/settings.py msgid "Guidance text" msgstr "Guida ai pagamenti" @@ -12535,8 +12618,8 @@ msgid "" msgstr "" "Salve {name},\n" "\n" -"è stato apportato il seguente cambiamento al tuo account presso {organizer}" -":\n" +"è stato apportato il seguente cambiamento al tuo account presso " +"{organizer}:\n" "\n" "{message}\n" "\n" @@ -13054,7 +13137,8 @@ msgstr "" #: pretix/base/settings.py msgid "" "You have to ask for invoice addresses if you want to make them required." -msgstr "Devi chiedere gli indirizzi della fattura se vuoi renderli obbligatori." +msgstr "" +"Devi chiedere gli indirizzi della fattura se vuoi renderli obbligatori." #: pretix/base/settings.py msgid "You have to require invoice addresses to require for company names." @@ -13090,7 +13174,8 @@ msgstr "" #: pretix/base/shredder.py msgid "Your ticket shop needs to be offline to use this feature." -msgstr "La tua biglietteria deve essere offline per utilizzare questa funzione." +msgstr "" +"La tua biglietteria deve essere offline per utilizzare questa funzione." #: pretix/base/shredder.py msgid "Phone numbers" @@ -13425,8 +13510,8 @@ msgid "" "You are receiving this email because you placed an order for " "%(event)s." msgstr "" -"Hai ricevuto questa email perché hai effettuato un ordine per %" -"(event)s." +"Hai ricevuto questa email perché hai effettuato un ordine per " +"%(event)s." #: pretix/base/templates/pretixbase/email/order_details.html #: pretix/control/templates/pretixcontrol/organizers/customer.html @@ -14259,6 +14344,28 @@ msgstr "" "Hai configurato le carte regalo per essere valide {} anni più l'anno in cui " "viene rilasciata la carta regalo." +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment date" +msgid "Payment term" +msgstr "Data pagamento" + +#: pretix/control/forms/event.py +msgid "same as above" +msgstr "" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in days" +msgid "different payment term in days" +msgstr "Termine di pagamento in giorni" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in minutes" +msgid "different payment term in minutes" +msgstr "Termine di pagamento in minuti" + #: pretix/control/forms/event.py msgid "Prices including tax" msgstr "Prezzi comprensivi di tasse" @@ -16817,7 +16924,8 @@ msgstr "La quota selezionata non corrisponde al sotto-evento selezionato." #: pretix/control/forms/vouchers.py msgid "There is no sufficient quota available to perform this change." -msgstr "Non è disponibile una quota sufficiente per effettuare questa modifica." +msgstr "" +"Non è disponibile una quota sufficiente per effettuare questa modifica." #: pretix/control/forms/vouchers.py msgid "" @@ -17038,8 +17146,8 @@ msgid "" "Position #{posid}: {old_item} ({old_price}) changed to {new_item} " "({new_price})." msgstr "" -"Posizione #{posid}: {old_item} ({old_price}) sostituita con {new_item} (" -"{new_price})." +"Posizione #{posid}: {old_item} ({old_price}) sostituita con {new_item} " +"({new_price})." #: pretix/control/logdisplay.py #, python-brace-format @@ -17072,15 +17180,15 @@ msgstr "" #, python-brace-format msgid "Tax rule of position #{posid} changed from {old_rule} to {new_rule}." msgstr "" -"Regola fiscale della posizione #{posid} aggiornata da {old_rule} a {new_rule}" -"." +"Regola fiscale della posizione #{posid} aggiornata da {old_rule} a " +"{new_rule}." #: pretix/control/logdisplay.py #, python-brace-format msgid "Tax rule of fee #{fee} changed from {old_rule} to {new_rule}." msgstr "" -"Regola fiscale della commissione #{fee} aggiornata da {old_rule} a {new_rule}" -"." +"Regola fiscale della commissione #{fee} aggiornata da {old_rule} a " +"{new_rule}." #: pretix/control/logdisplay.py msgid "A fee has been added" @@ -17110,7 +17218,8 @@ msgstr "Posizione #{posid} ({old_item}, {old_price}) annullata." msgid "" "Position #{posid} created: {item} ({price}) as an add-on to position " "#{addon_to}." -msgstr "Posizione #{posid} creata: {item} ({price}) come aggiunta a {addon_to}." +msgstr "" +"Posizione #{posid} creata: {item} ({price}) come aggiunta a {addon_to}." #: pretix/control/logdisplay.py #, python-brace-format @@ -17167,8 +17276,8 @@ msgid "" "Unknown scan of code \"{barcode}…\" at {datetime} for list \"{list}\", type " "\"{type}\"." msgstr "" -"Scansione sconosciuta del codice \"{barcode}…\" a {datetime} per la lista \"" -"{list}\", tipo \"{type}\"." +"Scansione sconosciuta del codice \"{barcode}…\" a {datetime} per la lista " +"\"{list}\", tipo \"{type}\"." #: pretix/control/logdisplay.py #, python-brace-format @@ -17211,8 +17320,8 @@ msgid "" "Denied scan of position #{posid} for list \"{list}\", type \"{type}\", error " "code \"{errorcode}\"." msgstr "" -"Scansione negata della posizione #{posid} per la lista \"{list}\", tipo \"" -"{type}\", codice di errore \"{errorcode}\"." +"Scansione negata della posizione #{posid} per la lista \"{list}\", tipo " +"\"{type}\", codice di errore \"{errorcode}\"." #: pretix/control/logdisplay.py #, python-brace-format @@ -17220,16 +17329,16 @@ msgid "" "Annulled scan of position #{posid} at {datetime} for list \"{list}\", type " "\"{type}\"." msgstr "" -"Scansione annullata della posizione #{posid} a {datetime} per la lista \"" -"{list}\", tipo \"{type}\"." +"Scansione annullata della posizione #{posid} a {datetime} per la lista " +"\"{list}\", tipo \"{type}\"." #: pretix/control/logdisplay.py #, python-brace-format msgid "" "Annulled scan of position #{posid} for list \"{list}\", type \"{type}\"." msgstr "" -"Scansione annullata della posizione #{posid} per la lista \"{list}\", tipo \"" -"{type}\"." +"Scansione annullata della posizione #{posid} per la lista \"{list}\", tipo " +"\"{type}\"." #: pretix/control/logdisplay.py #, python-brace-format @@ -17237,8 +17346,8 @@ msgid "" "Ignored annulment of position #{posid} at {datetime} for list \"{list}\", " "type \"{type}\"." msgstr "" -"Ignorata l'annullamento della posizione #{posid} a {datetime} per la lista \"" -"{list}\", tipo \"{type}\"." +"Ignorata l'annullamento della posizione #{posid} a {datetime} per la lista " +"\"{list}\", tipo \"{type}\"." #: pretix/control/logdisplay.py #, python-brace-format @@ -17278,8 +17387,8 @@ msgstr "" msgid "" "Position #{posid} has been checked in at {datetime} for list \"{list}\"." msgstr "" -"La posizione #{posid} ha effettuato il check-in il {datetime} nella lista \"" -"{list}\"." +"La posizione #{posid} ha effettuato il check-in il {datetime} nella lista " +"\"{list}\"." #: pretix/control/logdisplay.py #, python-brace-format @@ -17520,7 +17629,8 @@ msgstr "" #: pretix/control/logdisplay.py #, python-brace-format msgid "The invoice {full_invoice_no} has been scheduled for retransmission." -msgstr "La fattura {full_invoice_no} è stata programmata per la ritrasmissione." +msgstr "" +"La fattura {full_invoice_no} è stata programmata per la ritrasmissione." #: pretix/control/logdisplay.py msgid "The order's internal comment has been updated." @@ -18538,8 +18648,8 @@ msgstr "" msgid "" "Position #{posid} has been checked in again at {datetime} on list \"{list}\"." msgstr "" -"La posizione #{posid} è stata ricontrollata il {datetime} nella lista \"" -"{list}\"." +"La posizione #{posid} è stata ricontrollata il {datetime} nella lista " +"\"{list}\"." #: pretix/control/logdisplay.py msgid "An entry has been removed from the waiting list." @@ -18557,7 +18667,8 @@ msgstr "Un elemento è stato aggiunto alla lista d'attesa." msgid "" "The selected event was not found or you have no permission to administrate " "it." -msgstr "L'evento selezionato non esiste o non hai i permessi per amministrarlo." +msgstr "" +"L'evento selezionato non esiste o non hai i permessi per amministrarlo." #: pretix/control/middleware.py msgid "" @@ -19053,8 +19164,8 @@ msgid "" "We've detected that you are using Microsoft Internet Explorer." msgstr "" -"Abbiamo rilevato che stai usando Microsoft Internet Explorer" -"." +"Abbiamo rilevato che stai usando Microsoft Internet Explorer." #: pretix/control/templates/pretixcontrol/base.html #: pretix/presale/templates/pretixpresale/base.html @@ -19314,8 +19425,8 @@ msgstr[0] "" "Sei sicuro di voler eliminare permanentemente i check-in di un " "biglietto?" msgstr[1] "" -"Sei sicuro di voler eliminare permanentemente i check-in di %(count)" -"s biglietti?" +"Sei sicuro di voler eliminare permanentemente i check-in di " +"%(count)s biglietti?" #: pretix/control/templates/pretixcontrol/checkin/bulk_revert_confirm.html #: pretix/control/templates/pretixcontrol/checkin/list_delete.html @@ -19776,7 +19887,8 @@ msgid "This will permanently delete 1 check-in." msgid_plural "" "This will permanently delete %(count)s check-ins." msgstr[0] "Questo eliminerà definitivamente 1 check-in." -msgstr[1] "Questo eliminerà definitivamente %(count)s check-in." +msgstr[1] "" +"Questo eliminerà definitivamente %(count)s check-in." #: pretix/control/templates/pretixcontrol/checkin/reset.html #, python-format @@ -19784,7 +19896,8 @@ msgid "Additionally, 1 print log will be deleted." msgid_plural "" "Additionally, %(count)s print logs will be deleted." msgstr[0] "Inoltre, 1 log di stampa verrà eliminato." -msgstr[1] "Inoltre, %(count)s log di stampa verranno eliminati." +msgstr[1] "" +"Inoltre, %(count)s log di stampa verranno eliminati." #: pretix/control/templates/pretixcontrol/checkin/reset.html msgid "This cannot be reverted!" @@ -20062,8 +20175,8 @@ msgid "" msgstr "" "Ciao,\n" "\n" -"qualcuno ha richiesto di utilizzare %(address)s come indirizzo mittente su %" -"(instance)s. Dopo la verifica, le email inviate da %(instance)s potranno " +"qualcuno ha richiesto di utilizzare %(address)s come indirizzo mittente su " +"%(instance)s. Dopo la verifica, le email inviate da %(instance)s potranno " "mostrare questo indirizzo come mittente.\n" "\n" "Se sei stato tu, inserisci il seguente codice nel modulo di configurazione:\n" @@ -21313,6 +21426,7 @@ msgid "Top recommendation" msgstr "Top raccomandazione" #: pretix/control/templates/pretixcontrol/event/plugins.html +#: pretix/control/templates/pretixcontrol/items/questions.html #: pretix/control/templates/pretixcontrol/organizers/plugins.html #, fuzzy msgid "Experimental feature" @@ -21978,8 +22092,8 @@ msgid "" "%(plugin_settings_href)s>plugin settings and activate one or more ticket " "output plugins." msgstr "" -"Non sono disponibili uscite dei biglietti. Vai alle impostazioni plugin e attiva uno o più plugin di uscita dei " +"Non sono disponibili uscite dei biglietti. Vai alle impostazioni plugin e attiva uno o più plugin di uscita dei " "biglietti." #: pretix/control/templates/pretixcontrol/event/tickets.html @@ -23394,7 +23508,8 @@ msgstr "Sopprimere la domanda" #, fuzzy, python-format msgid "" "Are you sure you want to delete the question %(question)s?" -msgstr "Sei sicuro di voler eliminare la domanda %(question)s?" +msgstr "" +"Sei sicuro di voler eliminare la domanda %(question)s?" #: pretix/control/templates/pretixcontrol/items/question_delete.html #, fuzzy @@ -23402,8 +23517,8 @@ msgid "" "All answers to the question given by the buyers of the following products " "will be lost." msgstr "" -"Tutte le risposte fornite dagli acquirenti dei seguenti prodotti " -"andranno perse." +"Tutte le risposte fornite dagli acquirenti dei seguenti prodotti " +"andranno perse." #: pretix/control/templates/pretixcontrol/items/question_delete.html #, fuzzy, python-format @@ -23484,6 +23599,27 @@ msgstr "" "biglietto. Se offri cibo, un esempio potrebbe essere chiedere informazioni " "sulle esigenze alimentari." +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +msgid "Create a new per-ticket question" +msgstr "Crea una nuova domanda" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +msgid "Create a new order-level question" +msgstr "Crea una nuova domanda" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +msgid "Per-ticket questions" +msgstr "Sopprimere la domanda" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"These questions are asked for every ticket, so possibly multiple times in " +"the same order." +msgstr "" + #: pretix/control/templates/pretixcontrol/items/questions.html #, fuzzy msgid "Create a new question" @@ -23504,6 +23640,29 @@ msgstr "Chiedi al momento del check-in" msgid "All personalized products" msgstr "Limita ai prodotti" +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +msgid "Per-order questions" +msgstr "Includi domande" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"This functionality is in active development and expected to change " +"significantly over the coming months." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"Per-order questions are currently not supported and will not be displayed in " +"pretixPOS." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "The question has been reordered." +msgid "These questions are asked once per order." +msgstr "La domanda è stata riorganizzata." + #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html #, fuzzy, python-format @@ -23731,8 +23890,8 @@ msgid "" "Are you sure you want to disable the application %(application)s permanently?" msgstr "" -"Vuoi davvero disabilitare definitivamente l'applicazione %" -"(application)s?" +"Vuoi davvero disabilitare definitivamente l'applicazione " +"%(application)s?" #: pretix/control/templates/pretixcontrol/oauth/app_list.html #, fuzzy @@ -23766,8 +23925,8 @@ msgid "" "Are you sure you want to generate a new client secret for the application " "%(application)s?" msgstr "" -"Vuoi veramente generare un nuovo segreto client per l'applicazione %" -"(application)s?" +"Vuoi veramente generare un nuovo segreto client per l'applicazione " +"%(application)s?" #: pretix/control/templates/pretixcontrol/oauth/app_rollkeys.html #, fuzzy @@ -24172,6 +24331,14 @@ msgstr "Informazioni fattura" msgid "(optional)" msgstr "(facoltativo)" +#: pretix/control/templates/pretixcontrol/order/change_questions.html +#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html +#: pretix/presale/templates/pretixpresale/event/checkout_questions.html +#: pretix/presale/templates/pretixpresale/event/order_modify.html +#, fuzzy +msgid "Additional order information" +msgstr "Informazioni aggiuntive" + #: pretix/control/templates/pretixcontrol/order/delete.html #, fuzzy msgid "Delete order" @@ -24207,6 +24374,27 @@ msgstr "Sì, annulla l'ordine" msgid "Extend payment term" msgstr "Estendi il termine di pagamento" +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +#, fuzzy +msgid "" +"This file has been uploaded by a user and could contain viruses or other " +"malicious content." +msgstr "" +"Questo file è stato caricato da un utente e potrebbe contenere virus o altri " +"contenuti dannosi." + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "UNSAFE" +msgstr "NON SICURO" + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +#: pretix/control/templates/pretixcontrol/order/index.html +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +#, fuzzy +msgid "not answered" +msgstr "non ha risposto" + #: pretix/control/templates/pretixcontrol/order/index.html #, fuzzy, python-format msgid "Order details: %(code)s" @@ -24532,30 +24720,11 @@ msgstr "Valido %(datetime_range)s" msgid "Ticket page" msgstr "Check-in del biglietto effettuato" -#: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -#, fuzzy -msgid "not answered" -msgstr "non ha risposto" - #: pretix/control/templates/pretixcontrol/order/index.html #, fuzzy msgid "This question will be asked during check-in." msgstr "Questa domanda verrà posta durante il check-in." -#: pretix/control/templates/pretixcontrol/order/index.html -#, fuzzy -msgid "" -"This file has been uploaded by a user and could contain viruses or other " -"malicious content." -msgstr "" -"Questo file è stato caricato da un utente e potrebbe contenere virus o altri " -"contenuti dannosi." - -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "UNSAFE" -msgstr "NON SICURO" - #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/orders/overview.html #: pretix/plugins/reports/accountingreport.py @@ -25006,8 +25175,9 @@ msgid "" "%(allowed)s of the selected %(total)s " "orders." msgstr "" -"L'operazione %(label)s può essere applicata a %" -"(allowed)s degli %(total)s ordini selezionati." +"L'operazione %(label)s può essere applicata a " +"%(allowed)s degli %(total)s ordini " +"selezionati." #: pretix/control/templates/pretixcontrol/orders/bulk_action.html #, fuzzy @@ -26113,6 +26283,16 @@ msgstr "Collega un dispositivo" msgid "Hardware model" msgstr "Modello hardware" +#: pretix/control/templates/pretixcontrol/organizers/devices.html +#, fuzzy, python-format +#| msgid "Begin: %(time)s" +msgid "Last seen: %(time)s" +msgstr "Inizio: %(time)s" + +#: pretix/control/templates/pretixcontrol/organizers/devices.html +msgid "No recent contact" +msgstr "" + #: pretix/control/templates/pretixcontrol/organizers/devices.html #, fuzzy msgid "Not yet initialized" @@ -27968,8 +28148,8 @@ msgid "" "Using this option will delete all current quotas from " "all selected dates." msgstr "" -"Questa opzione eliminerà tutte le quote attuali da " -"tutte le date selezionate." +"Questa opzione eliminerà tutte le quote attuali da " +"tutte le date selezionate." #: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html #, fuzzy @@ -28220,7 +28400,8 @@ msgstr "" #: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html #, fuzzy msgid "In \"Secret\"/\"Account Key\", enter the following code:" -msgstr "Nel campo \"Segreto\"/\"Chiave account\", inserisci il seguente codice:" +msgstr "" +"Nel campo \"Segreto\"/\"Chiave account\", inserisci il seguente codice:" #: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html msgid "copy" @@ -28269,8 +28450,8 @@ msgstr "Elimina un dispositivo di autenticazione a due fattori" msgid "" "Are you sure you want to delete the authentication device \"%(device)s\"?" msgstr "" -"Sei sicuro di voler eliminare il dispositivo di autenticazione \"%(device)" -"s\"?" +"Sei sicuro di voler eliminare il dispositivo di autenticazione " +"\"%(device)s\"?" #: pretix/control/templates/pretixcontrol/user/2fa_delete.html #, fuzzy @@ -28317,7 +28498,8 @@ msgstr "Vuoi davvero abilitare l'autenticazione a due fattori?" msgid "" "You will no longer be able to log in to pretix without one of your " "configured devices." -msgstr "Non potrai più accedere a pretix senza uno dei dispositivi configurati." +msgstr "" +"Non potrai più accedere a pretix senza uno dei dispositivi configurati." #: pretix/control/templates/pretixcontrol/user/2fa_enable.html #, fuzzy @@ -28602,8 +28784,8 @@ msgid "" "We just want to make sure it's really you. Please re-authenticate with " "'%(login_provider)s'." msgstr "" -"Vogliamo solo essere sicuri che siate voi. Per favore ri-autenticate con '%" -"(login_provider)s'." +"Vogliamo solo essere sicuri che siate voi. Per favore ri-autenticate con " +"'%(login_provider)s'." #: pretix/control/templates/pretixcontrol/user/reauth.html #, fuzzy @@ -29026,8 +29208,8 @@ msgid "" "Are you sure you want to delete the following waiting list entry " "%(entry)s?" msgstr "" -"Sei sicuro di voler eliminare l'entry della lista d'attesa %(entry)" -"s?" +"Sei sicuro di voler eliminare l'entry della lista d'attesa " +"%(entry)s?" #: pretix/control/templates/pretixcontrol/waitinglist/delete_bulk.html #, fuzzy @@ -29895,11 +30077,6 @@ msgstr "L'ordine delle categorie è stato aggiornato." msgid "Not all objects have been selected." msgstr "Non sono stati selezionati tutti gli oggetti." -#: pretix/control/views/item.py -#, fuzzy -msgid "Street" -msgstr "Indirizzo" - #: pretix/control/views/item.py #, fuzzy msgid "The requested question does not exist." @@ -30099,7 +30276,7 @@ msgstr "" #: pretix/control/views/mailsetup.py #, fuzzy msgid "" -"We did not find DMARC record for your domain. This means that there is a " +"We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" @@ -30615,7 +30792,8 @@ msgstr "Oggetto: {subject}" msgid "Your message has been queued and will be sent to {}." msgstr "Il tuo messaggio è stato inserito in coda e verrà inviato a {}." -#: pretix/control/views/orders.py pretix/presale/views/order.py +#: pretix/control/views/orders.py pretix/presale/views/cart.py +#: pretix/presale/views/order.py #, fuzzy msgid "" "This link is no longer valid. Please go back, refresh the page, and try " @@ -31981,8 +32159,8 @@ msgstr "Stampa badge" msgid "" "Are you sure you want to delete the badge layout %(layout)s?" msgstr "" -"Sei sicuro di voler eliminare il layout del badge %(layout)s" -"?" +"Sei sicuro di voler eliminare il layout del badge %(layout)s?" #: pretix/plugins/badges/templates/pretixplugins/badges/edit.html #, fuzzy, python-format @@ -33280,6 +33458,29 @@ msgstr "" "pagare in rate o entro 30 giorni. Tu, come commerciante, riceverai il " "pagamento immediatamente." +#: pretix/plugins/paypal2/payment.py +msgid "Allow further payments during compliance hold" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"PayPals fraud prevention might block processing of individual payments for a " +"considerable amount of time. The payment is marked as \"pending\" during " +"this time window. You can allow your customers to start another payment " +"attempts during that window. This might result in them being charged twice " +"if theoriginal payment is approved." +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "Timeout further payment attempts" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"Time duration in minutes after which another payment attempt is possible, " +"while the last payment is still under investigation." +msgstr "" + #: pretix/plugins/paypal2/payment.py #, fuzzy msgid "-- Automatic --" @@ -33570,16 +33771,13 @@ msgid "Please select how you want to pay." msgstr "Scegli come intendi pagare." #: pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html -#, fuzzy -#| msgid "" -#| "Our attempt to execute your Payment via PayPal has failed. Please try " -#| "again or contact us." msgid "" -"Our attempt to execute your payment via PayPal has failed. Please try again " -"or contact us." +"Your payment is being processed by PayPal. This takes longer than usual. You " +"can wait until PayPal acknowledges the payment or you can try paying again " +"with this or another payment method. This might result in you being charged " +"twice in case PayPal allows your initial payment attempt. Please contact us " +"to resolve this case." msgstr "" -"Il tuo tentativo di pagamento tramite PayPal è fallito. Prova di nuovo o " -"contattaci." #: pretix/plugins/paypal2/views.py #, fuzzy @@ -34670,8 +34868,8 @@ msgid "" "The provided key \"%(value)s\" does not look valid. It should start with " "\"%(prefix)s\"." msgstr "" -"La chiave fornita \"%(value)s\" non sembra valida. Dovrebbe iniziare con \"%" -"(prefix)s\"." +"La chiave fornita \"%(value)s\" non sembra valida. Dovrebbe iniziare con " +"\"%(prefix)s\"." #: pretix/plugins/stripe/forms.py pretix/plugins/stripe/signals.py #, fuzzy @@ -34716,10 +34914,10 @@ msgid "" "automatically cancel orders when charges are refunded externally and to " "process asynchronous payment methods like SOFORT." msgstr "" -"Configurare un " -"Stripe Webhook all'endpoint seguente per annullare automaticamente gli " -"ordini quando le spese vengono rimborsate esternamente e per elaborare " -"metodi di pagamento asincroni come SOFORT." +"Configurare un Stripe Webhook all'endpoint seguente per annullare " +"automaticamente gli ordini quando le spese vengono rimborsate esternamente e " +"per elaborare metodi di pagamento asincroni come SOFORT." #: pretix/plugins/stripe/payment.py #, fuzzy @@ -34899,9 +35097,9 @@ msgid "" "allows for this lag." msgstr "" "I pagamenti SEPA Direct Debit tramite Stripe non vengono " -"elaborati immediatamente e in alcuni casi possono richiedere fino a " -"14 giorni per essere confermati. Attiva questo metodo soltanto se i " -"termini di pagamento consentono tale ritardo." +"elaborati immediatamente e in alcuni casi possono richiedere fino a " +"14 giorni per essere confermati. Attiva questo metodo " +"soltanto se i termini di pagamento consentono tale ritardo." #: pretix/plugins/stripe/payment.py #, fuzzy @@ -35732,7 +35930,8 @@ msgstr "Stiamo confermando il tuo pagamento…" #: pretix/plugins/stripe/views.py #, fuzzy msgid "An error occurred during connecting with Stripe, please try again." -msgstr "Si è verificato un errore durante la connessione con Stripe, riprovare." +msgstr "" +"Si è verificato un errore durante la connessione con Stripe, riprovare." #: pretix/plugins/stripe/views.py #, fuzzy, python-brace-format @@ -36399,9 +36598,9 @@ msgid "" "href=\"%(time_machine_link)s\">time machine." msgstr "" -"Per vedere il tuo negozio in momenti diversi, attiva la macchina del tempo." +"Per vedere il tuo negozio in momenti diversi, attiva la macchina del tempo." #: pretix/presale/templates/pretixpresale/event/base.html #, fuzzy @@ -36734,7 +36933,8 @@ msgstr "Se prosegui, potresti pagare un ordine reale con denaro inesistente!" #: pretix/presale/templates/pretixpresale/event/checkout_payment.html #, fuzzy msgid "This payment provider does not provide support for test mode." -msgstr "Questo fornitore di servizi di pagamento non supporta la modalità test." +msgstr "" +"Questo fornitore di servizi di pagamento non supporta la modalità test." #: pretix/presale/templates/pretixpresale/event/checkout_payment.html #, fuzzy @@ -36816,7 +37016,8 @@ msgstr "" #, fuzzy, python-format msgid "You need to choose exactly one option from this category." msgid_plural "You need to choose %(min_count)s options from this category." -msgstr[0] "È necessario selezionare esattamente un'opzione in questa categoria." +msgstr[0] "" +"È necessario selezionare esattamente un'opzione in questa categoria." msgstr[1] "È necessario selezionare %(min_count)s opzioni in questa categoria." #: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html @@ -37048,11 +37249,6 @@ msgstr[1] "Questo biglietto è stato usato %(count)s volte." msgid "No attendee name provided" msgstr "Nome partecipante" -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -#, fuzzy -msgid "The image you previously uploaded" -msgstr "L'immagine precedentemente caricata" - #: pretix/presale/templates/pretixpresale/event/fragment_cart.html #, fuzzy msgid "The price of this product was reduced because of an automatic discount." @@ -37514,6 +37710,11 @@ msgstr "%(value)s senza tasse" msgid "Set price in %(currency)s for %(item)s" msgstr "Imposta il prezzo in %(currency)s per %(item)s" +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +#, fuzzy +msgid "The image you previously uploaded" +msgstr "L'immagine precedentemente caricata" + #: pretix/presale/templates/pretixpresale/event/fragment_quota_left.html #, python-format msgid "%(num)s currently available" @@ -37635,8 +37836,8 @@ msgstr "Il periodo di prevendita per questo evento è concluso." #, fuzzy, python-format msgid "The booking period for this event will start on %(date)s at %(time)s." msgstr "" -"Il periodo di prenotazione per questo evento inizierà il %(date)s alle %" -"(time)s." +"Il periodo di prenotazione per questo evento inizierà il %(date)s alle " +"%(time)s." #: pretix/presale/templates/pretixpresale/event/index.html #: pretix/presale/templates/pretixpresale/event/seatingplan.html @@ -38663,8 +38864,8 @@ msgid "" "If you're looking to configure this installation, please head " "over here." msgstr "" -"Se stai cercando di configurare questa installazione, ti preghiamo di andare qui." +"Se stai cercando di configurare questa installazione, ti preghiamo di andare qui." #: pretix/presale/templates/pretixpresale/index.html #, fuzzy @@ -39452,6 +39653,17 @@ msgstr "Accesso in scrittura" msgid "Kosovo" msgstr "Kosovo" +#, fuzzy +#~| msgid "" +#~| "Our attempt to execute your Payment via PayPal has failed. Please try " +#~| "again or contact us." +#~ msgid "" +#~ "Our attempt to execute your payment via PayPal has failed. Please try " +#~ "again or contact us." +#~ msgstr "" +#~ "Il tuo tentativo di pagamento tramite PayPal è fallito. Prova di nuovo o " +#~ "contattaci." + #~ msgid "Account invitation" #~ msgstr "Invito account" @@ -39547,9 +39759,6 @@ msgstr "Kosovo" #~ msgid "Please do not use special characters in names." #~ msgstr "Per favore non usare caratteri speciali nei nomi." -#~ msgid "State" -#~ msgstr "Stato" - #~ msgid "Program times: date and time" #~ msgstr "Orari del programma: data e ora" diff --git a/src/pretix/locale/ja/LC_MESSAGES/django.po b/src/pretix/locale/ja/LC_MESSAGES/django.po index 9125c0370..ad348c57f 100644 --- a/src/pretix/locale/ja/LC_MESSAGES/django.po +++ b/src/pretix/locale/ja/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: 2026-08-16 22:00+0000\n" "Last-Translator: Hijiri Umemoto \n" "Language-Team: Japanese \n" "Language-Team: Korean \n" "Language-Team: Luxembourgish \n" "Language-Team: Latvian \n" "Language-Team: Norwegian Bokmål \n" "Language-Team: Dutch \n" "Language-Team: Dutch (Belgium) \n" "Language-Team: Dutch (informal) \n" -"Language-Team: Polish \n" +"Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -92,6 +92,10 @@ msgstr "Grecki" msgid "Hebrew" msgstr "Hebrajski" +#: pretix/_base_settings.py +msgid "Hungarian" +msgstr "" + #: pretix/_base_settings.py msgid "Indonesian" msgstr "Indonezyjski" @@ -445,6 +449,12 @@ msgstr "Nośnik podpięty do innego wydarzenia" msgid "You cannot exchange a medium for a medium." msgstr "Nie możesz zmienić tego zamówienia." +#: pretix/api/views/checkin.py +#, fuzzy +#| msgid "You cannot change this order." +msgid "You cannot simulate a medium exchange." +msgstr "Nie możesz zmienić tego zamówienia." + #: pretix/api/views/oauth.py pretix/control/logdisplay.py #, python-brace-format msgid "" @@ -917,8 +927,7 @@ msgstr "Wyślij informacje o odzyskiwaniu" #: pretix/control/forms/organizer.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/organizers/customer.html -#: pretix/control/views/item.py pretix/plugins/badges/exporters.py -#: pretix/plugins/checkinlists/exporters.py +#: pretix/plugins/badges/exporters.py pretix/plugins/checkinlists/exporters.py #: pretix/plugins/ticketoutputpdf/exporters.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html #: pretix/presale/templates/pretixpresale/event/fragment_change_form.html @@ -937,7 +946,6 @@ msgstr "Imię i nazwisko uczestnika" #: pretix/base/forms/questions.py pretix/base/models/customers.py #: pretix/base/models/orders.py pretix/base/pdf.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "Attendee email" msgstr "Adres email uczestnika" @@ -1731,9 +1739,8 @@ msgid "Invoice sender:" msgstr "Nadawca faktury:" #: pretix/base/exporters/invoices.py pretix/base/exporters/orderlist.py -#: pretix/base/forms/questions.py pretix/base/modelimport_orders.py -#: pretix/base/models/customers.py pretix/base/models/orders.py -#: pretix/control/forms/filter.py +#: pretix/base/modelimport_orders.py pretix/base/models/customers.py +#: pretix/base/models/orders.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html @@ -1745,8 +1752,7 @@ msgstr "Adres" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "ZIP code" msgstr "Kod pocztowy" @@ -1754,8 +1760,7 @@ msgstr "Kod pocztowy" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/base/models/customers.py pretix/base/models/orders.py #: pretix/base/settings.py pretix/control/forms/filter.py -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py msgid "City" msgstr "Miasto" @@ -1778,8 +1783,7 @@ msgstr "Stan (dot. USA)" #: pretix/base/settings.py pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/email/login_notice.txt #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Country" @@ -1807,8 +1811,7 @@ msgstr "Odbiorca faktury:" #: pretix/base/forms/questions.py pretix/base/modelimport_orders.py #: pretix/control/forms/filter.py #: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/control/views/item.py pretix/plugins/checkinlists/exporters.py -#: pretix/plugins/reports/exporters.py +#: pretix/plugins/checkinlists/exporters.py pretix/plugins/reports/exporters.py #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/order.html msgid "Company" @@ -2624,7 +2627,8 @@ msgstr "Stany płatności" msgid "Refund states" msgstr "Stany zwrotów" -#: pretix/base/exporters/orderlist.py pretix/base/models/orders.py +#: pretix/base/exporters/orderlist.py pretix/base/models/items.py +#: pretix/base/models/orders.py #: pretix/control/templates/pretixcontrol/datasync/failed_jobs.html #: pretix/control/templates/pretixcontrol/organizers/customer.html #: pretix/control/templates/pretixcontrol/organizers/outgoing_mail.html @@ -3152,14 +3156,12 @@ msgstr "" "lub jest uszkodzony." #: pretix/base/forms/questions.py -msgid "" -"If you keep this empty, the ticket will be valid starting at the time of " -"purchase." -msgstr "Jeśli to pole pozostanie puste, bilet będzie ważny od momentu zakupu." +msgid "Street" +msgstr "Ulica" #: pretix/base/forms/questions.py -msgid "Street and Number" -msgstr "Ulica i numer domu" +msgid "State" +msgstr "Państwo" #: pretix/base/forms/questions.py #, fuzzy, python-brace-format @@ -3196,6 +3198,16 @@ msgstr "Wprowadź dostępny kanał sprzedaży." msgid "Please enter a date and time no later than {max}." msgstr "Wprowadź prawidłowy wynik." +#: pretix/base/forms/questions.py +msgid "" +"If you keep this empty, the ticket will be valid starting at the time of " +"purchase." +msgstr "Jeśli to pole pozostanie puste, bilet będzie ważny od momentu zakupu." + +#: pretix/base/forms/questions.py +msgid "Street and Number" +msgstr "Ulica i numer domu" + #: pretix/base/forms/questions.py msgid "" "Optional, but depending on the country you reside in we might need to charge " @@ -5846,6 +5858,10 @@ msgstr "Wybrany wariant nie należy do tej pozycji." msgid "The count needs to be equal to or greater than zero." msgstr "Liczba musi być równa lub większa od zera." +#: pretix/base/models/items.py pretix/base/models/orders.py +msgid "Order position" +msgstr "Pozycja zamówienia" + #: pretix/base/models/items.py msgid "Number" msgstr "Liczba" @@ -5883,6 +5899,10 @@ msgstr "Data i godzina" msgid "Country code (ISO 3166-1 alpha-2)" msgstr "Kod kraju (ISO 3166-1 alpha-2)" +#: pretix/base/models/items.py +msgid "Asked on" +msgstr "" + #: pretix/base/models/items.py pretix/base/models/organizer.py msgid "" "The identifier may only contain letters, numbers, dots, dashes, and " @@ -6551,10 +6571,6 @@ msgstr "Karta prezentowa" msgid "Value" msgstr "Wartość" -#: pretix/base/models/orders.py -msgid "Order position" -msgstr "Pozycja zamówienia" - #: pretix/base/models/orders.py msgid "Cart ID (e.g. session key)" msgstr "ID wózka (np. klucz sesji)" @@ -7584,8 +7600,11 @@ msgid "The payment for this invoice has already been received." msgstr "Płatność za tę fakturę została już otrzymana." #: pretix/base/payment.py +#, fuzzy +#| msgid "" +#| "This payment is already being processed and can not be canceled any more." msgid "" -"This payment is already being processed and can not be canceled any more." +"This payment is already being processed and cannot be canceled any more." msgstr "Ta płatność jest już przetwarzana i nie można jej anulować." #: pretix/base/payment.py @@ -8392,6 +8411,18 @@ msgstr "Rozpoczęcie przedsprzedaży" msgid "Presale end" msgstr "Koniec przedsprzedaży" +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order details" +msgid "Order creation" +msgstr "Szczegóły zamówienia" + +#: pretix/base/reldate.py +#, fuzzy +#| msgid "Order expired" +msgid "Order expiry" +msgstr "Zamówienie wygasło" + #: pretix/base/reldate.py msgid "before" msgstr "przed" @@ -8416,6 +8447,36 @@ msgstr "Czas względny:" msgid "Not set" msgstr "Nie ustawiony" +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"before\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative time cannot be expressed as \"after\" for \"{}\"" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'before' for '{}'" +msgstr "" + +#: pretix/base/reldate.py +#, python-brace-format +msgid "A relative date cannot be expressed as 'after' for '{}'" +msgstr "" + #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" msgstr "Losowo (wartość domyślna, działa ze wszystkimi aplikacjami pretix)" @@ -9722,7 +9783,7 @@ msgid "Please transfer money to this bank account: 9999-9999-9999-9999" msgstr "" "Proszę przelej pieniądze na następujące konto bankowe: 9999-9999-9999-9999" -#: pretix/base/services/placeholders.py pretix/control/views/organizer.py +#: pretix/base/services/placeholders.py msgid "This value will be replaced based on dynamic parameters." msgstr "Wartość ta zostanie zastąpiona na podstawie parametrów dynamicznych." @@ -10447,6 +10508,25 @@ msgstr "" "opisania innych opcji uzyskania biletu, takich jak kasa przed wejściem na " "wydarzenie." +#: pretix/base/settings.py +msgid "No dates match your criteria." +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "Text for empty date results" +msgstr "" + +#: pretix/base/settings.py +msgctxt "subevents" +msgid "" +"This text will be shown if the calendar or list of dates is empty, e.g. " +"because a month does not contain any dates or a filter chosen by the user " +"does not find any results. You can use this to advertise ways to get in " +"touch with you to arrange further dates. We do not recommend more than one " +"or two sentences." +msgstr "" + #: pretix/base/settings.py msgid "Guidance text" msgstr "Tekst poradnika" @@ -14463,6 +14543,28 @@ msgstr "" "Skonfigurowałeś karty podarunkowe, aby były ważne {} lata plus rok, w którym " "wydano kartę podarunkowoą." +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment date" +msgid "Payment term" +msgstr "Termin płatności" + +#: pretix/control/forms/event.py +msgid "same as above" +msgstr "" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in days" +msgid "different payment term in days" +msgstr "Termin płatności w dniach" + +#: pretix/control/forms/event.py +#, fuzzy +#| msgid "Payment term in minutes" +msgid "different payment term in minutes" +msgstr "Termin płatności w minutach" + #: pretix/control/forms/event.py #, fuzzy #| msgid "including all taxes" @@ -21440,6 +21542,7 @@ msgid "Top recommendation" msgstr "Najlepsza rekomendacja" #: pretix/control/templates/pretixcontrol/event/plugins.html +#: pretix/control/templates/pretixcontrol/items/questions.html #: pretix/control/templates/pretixcontrol/organizers/plugins.html msgid "Experimental feature" msgstr "Funkcja eksperymentalna" @@ -23357,6 +23460,30 @@ msgstr "" "Jeśli zapewniasz jedzenie, jednym z przykładów może być pytanie użytkowników " "o wymagania dietetyczne." +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new question" +msgid "Create a new per-ticket question" +msgstr "Utwórz nowe pytanie" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new question" +msgid "Create a new order-level question" +msgstr "Utwórz nowe pytanie" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Delete question" +msgid "Per-ticket questions" +msgstr "Usuń pytanie" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"These questions are asked for every ticket, so possibly multiple times in " +"the same order." +msgstr "" + #: pretix/control/templates/pretixcontrol/items/questions.html msgid "Create a new question" msgstr "Utwórz nowe pytanie" @@ -23373,6 +23500,30 @@ msgstr "Zapytaj podczas odprawy" msgid "All personalized products" msgstr "Wszystkie spersonalizowane produkty" +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Include questions" +msgid "Per-order questions" +msgstr "Dołącz pytania" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"This functionality is in active development and expected to change " +"significantly over the coming months." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"Per-order questions are currently not supported and will not be displayed in " +"pretixPOS." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "The question has been reordered." +msgid "These questions are asked once per order." +msgstr "Zmieniono kolejność pytania." + #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html #, python-format @@ -23977,6 +24128,15 @@ msgstr "Dane do faktury" msgid "(optional)" msgstr "(opcjonalne)" +#: pretix/control/templates/pretixcontrol/order/change_questions.html +#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html +#: pretix/presale/templates/pretixpresale/event/checkout_questions.html +#: pretix/presale/templates/pretixpresale/event/order_modify.html +#, fuzzy +#| msgid "Additional information" +msgid "Additional order information" +msgstr "Dodatkowe informacje" + #: pretix/control/templates/pretixcontrol/order/delete.html msgid "Delete order" msgstr "Usuń zamówienie" @@ -24006,6 +24166,25 @@ msgstr "Tak, odmów przyjęcia zmówienia" msgid "Extend payment term" msgstr "Wydłuż termin płatności" +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "" +"This file has been uploaded by a user and could contain viruses or other " +"malicious content." +msgstr "" +"Ten plik został przesłany przez użytkownika i może zawierać wirusy lub inną " +"złośliwą zawartość." + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "UNSAFE" +msgstr "NIEBEZPIECZNY" + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +#: pretix/control/templates/pretixcontrol/order/index.html +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "not answered" +msgstr "brak odpowiedzi" + #: pretix/control/templates/pretixcontrol/order/index.html #, python-format msgid "Order details: %(code)s" @@ -24301,27 +24480,10 @@ msgstr "Ważny %(datetime_range)s" msgid "Ticket page" msgstr "Strona z biletami" -#: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "not answered" -msgstr "brak odpowiedzi" - #: pretix/control/templates/pretixcontrol/order/index.html msgid "This question will be asked during check-in." msgstr "To pytanie zostanie zadane podczas zameldowania." -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "" -"This file has been uploaded by a user and could contain viruses or other " -"malicious content." -msgstr "" -"Ten plik został przesłany przez użytkownika i może zawierać wirusy lub inną " -"złośliwą zawartość." - -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "UNSAFE" -msgstr "NIEBEZPIECZNY" - #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/orders/overview.html #: pretix/plugins/reports/accountingreport.py @@ -25705,6 +25867,16 @@ msgstr "Podłącz urządzenie" msgid "Hardware model" msgstr "Model sprzętu" +#: pretix/control/templates/pretixcontrol/organizers/devices.html +#, fuzzy, python-format +#| msgid "Begin: %(time)s" +msgid "Last seen: %(time)s" +msgstr "Początek: %(time)s" + +#: pretix/control/templates/pretixcontrol/organizers/devices.html +msgid "No recent contact" +msgstr "" + #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "Not yet initialized" msgstr "Jeszcze nie zainicjowany" @@ -29012,10 +29184,6 @@ msgstr "Kolejność kategorii została zaktualizowana." msgid "Not all objects have been selected." msgstr "Nie wszystkie obiekty zostały wybrane." -#: pretix/control/views/item.py -msgid "Street" -msgstr "Ulica" - #: pretix/control/views/item.py msgid "The requested question does not exist." msgstr "Żądane pytanie nie istnieje." @@ -29219,7 +29387,7 @@ msgstr "" #| "You should update the DNS settings of your domain to include this system " #| "in the SPF record." msgid "" -"We did not find DMARC record for your domain. This means that there is a " +"We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" @@ -29675,7 +29843,8 @@ msgstr "Temat: {subject}" msgid "Your message has been queued and will be sent to {}." msgstr "Wiadomość została umieszczona w kolejce i zostanie wysłana do {}." -#: pretix/control/views/orders.py pretix/presale/views/order.py +#: pretix/control/views/orders.py pretix/presale/views/cart.py +#: pretix/presale/views/order.py msgid "" "This link is no longer valid. Please go back, refresh the page, and try " "again." @@ -31984,6 +32153,29 @@ msgstr "" "płatności w wielu ratach lub w ciągu 30 dni. Ty, jako sprzedawca, " "otrzymujesz pieniądze od razu." +#: pretix/plugins/paypal2/payment.py +msgid "Allow further payments during compliance hold" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"PayPals fraud prevention might block processing of individual payments for a " +"considerable amount of time. The payment is marked as \"pending\" during " +"this time window. You can allow your customers to start another payment " +"attempts during that window. This might result in them being charged twice " +"if theoriginal payment is approved." +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "Timeout further payment attempts" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"Time duration in minutes after which another payment attempt is possible, " +"while the last payment is still under investigation." +msgstr "" + #: pretix/plugins/paypal2/payment.py msgid "-- Automatic --" msgstr "-- Automatyczny --" @@ -32243,11 +32435,12 @@ msgstr "Wybierz, jak chcesz zapłacić." #: pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html msgid "" -"Our attempt to execute your payment via PayPal has failed. Please try again " -"or contact us." +"Your payment is being processed by PayPal. This takes longer than usual. You " +"can wait until PayPal acknowledges the payment or you can try paying again " +"with this or another payment method. This might result in you being charged " +"twice in case PayPal allows your initial payment attempt. Please contact us " +"to resolve this case." msgstr "" -"Nasza próba wykonania płatności za pośrednictwem systemu PayPal nie powiodła " -"się. Spróbuj ponownie lub skontaktuj się z nami." #: pretix/plugins/paypal2/views.py msgid "" @@ -35351,10 +35544,6 @@ msgstr[2] "Ten bilet został użyty %(count)s razy." msgid "No attendee name provided" msgstr "Brak nazwiska uczestnika" -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "The image you previously uploaded" -msgstr "Obraz przesłany wcześniej" - #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "The price of this product was reduced because of an automatic discount." msgstr "Cena tego produktu została obniżona z powodu automatycznego rabatu." @@ -35787,6 +35976,10 @@ msgstr "%(value)s bez podatku" msgid "Set price in %(currency)s for %(item)s" msgstr "Ustaw cenę w %(currency)s dla %(item)s" +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "The image you previously uploaded" +msgstr "Obraz przesłany wcześniej" + #: pretix/presale/templates/pretixpresale/event/fragment_quota_left.html #, python-format msgid "%(num)s currently available" @@ -37575,6 +37768,13 @@ msgstr "Dostęp do zapisu" msgid "Kosovo" msgstr "Kosovo" +#~ msgid "" +#~ "Our attempt to execute your payment via PayPal has failed. Please try " +#~ "again or contact us." +#~ msgstr "" +#~ "Nasza próba wykonania płatności za pośrednictwem systemu PayPal nie " +#~ "powiodła się. Spróbuj ponownie lub skontaktuj się z nami." + #~ msgid "All actions" #~ msgstr "Wszystkie działania" @@ -37723,9 +37923,6 @@ msgstr "Kosovo" #~ msgid "Please do not use special characters in names." #~ msgstr "Nie możesz używać znaków specjalnych w imieniu lub nazwisku." -#~ msgid "State" -#~ msgstr "Państwo" - #, fuzzy #~| msgid "Printing date and time" #~ msgid "Program times: date and time" diff --git a/src/pretix/locale/pl_Informal/LC_MESSAGES/django.po b/src/pretix/locale/pl_Informal/LC_MESSAGES/django.po index f65b2e78b..9c0f81a57 100644 --- a/src/pretix/locale/pl_Informal/LC_MESSAGES/django.po +++ b/src/pretix/locale/pl_Informal/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: 2025-01-20 01:00+0000\n" "Last-Translator: Serge Bazanski \n" "Language-Team: Polish (informal) \n" "Language-Team: Portuguese \n" "Language-Team: Portuguese (Brazil) \n" "Language-Team: Portuguese (Portugal) \n" "Language-Team: Romanian \n" "Language-Team: Russian \n" "Language-Team: Sinhala \n" "Language-Team: Slovak \n" "Language-Team: Slovenian \n" "Language-Team: Albanian \n" "Language-Team: Swedish en " "biljett?" msgstr[1] "" -"Är du säker på att du vill permanent radera incheckningarna för %" -"(count)s biljetter?" +"Är du säker på att du vill permanent radera incheckningarna för " +"%(count)s biljetter?" #: pretix/control/templates/pretixcontrol/checkin/bulk_revert_confirm.html #: pretix/control/templates/pretixcontrol/checkin/list_delete.html @@ -21385,6 +21484,7 @@ msgid "Top recommendation" msgstr "Topprekommendation" #: pretix/control/templates/pretixcontrol/event/plugins.html +#: pretix/control/templates/pretixcontrol/items/questions.html #: pretix/control/templates/pretixcontrol/organizers/plugins.html msgid "Experimental feature" msgstr "Experimentell funktion" @@ -23312,6 +23412,30 @@ msgstr "" "tillhandahåller mat kan ett exempel vara att fråga dina användare om " "kostbehov." +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new question" +msgid "Create a new per-ticket question" +msgstr "Skapa en ny fråga" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Create a new question" +msgid "Create a new order-level question" +msgstr "Skapa en ny fråga" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Delete question" +msgid "Per-ticket questions" +msgstr "Radera fråga" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"These questions are asked for every ticket, so possibly multiple times in " +"the same order." +msgstr "" + #: pretix/control/templates/pretixcontrol/items/questions.html msgid "Create a new question" msgstr "Skapa en ny fråga" @@ -23328,6 +23452,30 @@ msgstr "Fråga vid incheckning" msgid "All personalized products" msgstr "Alla personliga produkter" +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "Include questions" +msgid "Per-order questions" +msgstr "Inkludera frågor" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"This functionality is in active development and expected to change " +"significantly over the coming months." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +msgid "" +"Per-order questions are currently not supported and will not be displayed in " +"pretixPOS." +msgstr "" + +#: pretix/control/templates/pretixcontrol/items/questions.html +#, fuzzy +#| msgid "The question has been reordered." +msgid "These questions are asked once per order." +msgstr "Frågan har ordnats om." + #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html #, python-format @@ -23928,6 +24076,15 @@ msgstr "Fakturainformation" msgid "(optional)" msgstr "(valfritt)" +#: pretix/control/templates/pretixcontrol/order/change_questions.html +#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html +#: pretix/presale/templates/pretixpresale/event/checkout_questions.html +#: pretix/presale/templates/pretixpresale/event/order_modify.html +#, fuzzy +#| msgid "Additional information" +msgid "Additional order information" +msgstr "Ytterligare information" + #: pretix/control/templates/pretixcontrol/order/delete.html msgid "Delete order" msgstr "Radera bokning" @@ -23957,6 +24114,25 @@ msgstr "Ja, neka bokning" msgid "Extend payment term" msgstr "Förläng betalningsvillkor" +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "" +"This file has been uploaded by a user and could contain viruses or other " +"malicious content." +msgstr "" +"Den här filen har laddats upp av en användare och kan innehålla virus eller " +"annat skadligt innehåll." + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +msgid "UNSAFE" +msgstr "OSÄKER" + +#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html +#: pretix/control/templates/pretixcontrol/order/index.html +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "not answered" +msgstr "utan svar" + #: pretix/control/templates/pretixcontrol/order/index.html #, python-format msgid "Order details: %(code)s" @@ -24253,27 +24429,10 @@ msgstr "Giltigt %(datetime_range)s" msgid "Ticket page" msgstr "Klient-hemlighet" -#: pretix/control/templates/pretixcontrol/order/index.html -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "not answered" -msgstr "utan svar" - #: pretix/control/templates/pretixcontrol/order/index.html msgid "This question will be asked during check-in." msgstr "Denna fråga kommer att ställas under incheckningen." -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "" -"This file has been uploaded by a user and could contain viruses or other " -"malicious content." -msgstr "" -"Den här filen har laddats upp av en användare och kan innehålla virus eller " -"annat skadligt innehåll." - -#: pretix/control/templates/pretixcontrol/order/index.html -msgid "UNSAFE" -msgstr "OSÄKER" - #: pretix/control/templates/pretixcontrol/order/index.html #: pretix/control/templates/pretixcontrol/orders/overview.html #: pretix/plugins/reports/accountingreport.py @@ -25656,6 +25815,16 @@ msgstr "Anslut en enhet" msgid "Hardware model" msgstr "Hårdvarumodell" +#: pretix/control/templates/pretixcontrol/organizers/devices.html +#, fuzzy, python-format +#| msgid "Begin: %(time)s" +msgid "Last seen: %(time)s" +msgstr "Börjar: %(time)s" + +#: pretix/control/templates/pretixcontrol/organizers/devices.html +msgid "No recent contact" +msgstr "" + #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "Not yet initialized" msgstr "Ännu inte initialiserad" @@ -28967,10 +29136,6 @@ msgstr "Ordningen på kategorierna har uppdaterats." msgid "Not all objects have been selected." msgstr "Inte alla objekt har valts ut." -#: pretix/control/views/item.py -msgid "Street" -msgstr "Gata" - #: pretix/control/views/item.py msgid "The requested question does not exist." msgstr "Den begärda frågan finns inte." @@ -29172,7 +29337,7 @@ msgstr "" #| "You should update the DNS settings of your domain to include this system " #| "in the SPF record." msgid "" -"We did not find DMARC record for your domain. This means that there is a " +"We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" @@ -29626,7 +29791,8 @@ msgstr "Ämne: {subject}" msgid "Your message has been queued and will be sent to {}." msgstr "Ditt meddelande har lagts i kön och kommer att skickas till {}." -#: pretix/control/views/orders.py pretix/presale/views/order.py +#: pretix/control/views/orders.py pretix/presale/views/cart.py +#: pretix/presale/views/order.py msgid "" "This link is no longer valid. Please go back, refresh the page, and try " "again." @@ -31973,6 +32139,29 @@ msgstr "" "betala i flera avbetalningar eller inom 30 dagar. Du som handlare får dina " "pengar direkt." +#: pretix/plugins/paypal2/payment.py +msgid "Allow further payments during compliance hold" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"PayPals fraud prevention might block processing of individual payments for a " +"considerable amount of time. The payment is marked as \"pending\" during " +"this time window. You can allow your customers to start another payment " +"attempts during that window. This might result in them being charged twice " +"if theoriginal payment is approved." +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "Timeout further payment attempts" +msgstr "" + +#: pretix/plugins/paypal2/payment.py +msgid "" +"Time duration in minutes after which another payment attempt is possible, " +"while the last payment is still under investigation." +msgstr "" + #: pretix/plugins/paypal2/payment.py msgid "-- Automatic --" msgstr "-- Automatiskt --" @@ -32232,11 +32421,12 @@ msgstr "Välj hur du vill betala." #: pretix/plugins/paypal2/templates/pretixplugins/paypal2/pending.html msgid "" -"Our attempt to execute your payment via PayPal has failed. Please try again " -"or contact us." +"Your payment is being processed by PayPal. This takes longer than usual. You " +"can wait until PayPal acknowledges the payment or you can try paying again " +"with this or another payment method. This might result in you being charged " +"twice in case PayPal allows your initial payment attempt. Please contact us " +"to resolve this case." msgstr "" -"Vårt försök att genomföra din betalning via PayPal har misslyckats. Vänligen " -"försök igen eller kontakta oss." #: pretix/plugins/paypal2/views.py msgid "" @@ -35331,10 +35521,6 @@ msgstr[1] "Den här biljetten har använts %(count)s gånger." msgid "No attendee name provided" msgstr "Inget deltagarnamn angivet" -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html -msgid "The image you previously uploaded" -msgstr "Bilden du tidigare laddade upp" - #: pretix/presale/templates/pretixpresale/event/fragment_cart.html msgid "The price of this product was reduced because of an automatic discount." msgstr "Priset på denna produkt har sänkts på grund av en automatisk rabatt." @@ -35774,6 +35960,10 @@ msgstr "%(value)s exklusive skatter" msgid "Set price in %(currency)s for %(item)s" msgstr "Ange pris i %(currency)s för %(item)s" +#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html +msgid "The image you previously uploaded" +msgstr "Bilden du tidigare laddade upp" + #: pretix/presale/templates/pretixpresale/event/fragment_quota_left.html #, python-format msgid "%(num)s currently available" @@ -37575,6 +37765,13 @@ msgstr "Skrivrättigheter" msgid "Kosovo" msgstr "Kosovo" +#~ msgid "" +#~ "Our attempt to execute your payment via PayPal has failed. Please try " +#~ "again or contact us." +#~ msgstr "" +#~ "Vårt försök att genomföra din betalning via PayPal har misslyckats. " +#~ "Vänligen försök igen eller kontakta oss." + #~ msgid "All actions" #~ msgstr "Alla åtgärder" @@ -37726,9 +37923,6 @@ msgstr "Kosovo" #~ msgid "Please do not use special characters in names." #~ msgstr "Var snäll och använd inte specialtecken i namnen." -#~ msgid "State" -#~ msgstr "Region" - #, fuzzy #~| msgid "Printing date and time" #~ msgid "Program times: date and time" diff --git a/src/pretix/locale/th/LC_MESSAGES/django.po b/src/pretix/locale/th/LC_MESSAGES/django.po index 3a9383634..4c9abd1ca 100644 --- a/src/pretix/locale/th/LC_MESSAGES/django.po +++ b/src/pretix/locale/th/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: 2026-05-20 10:58+0000\n" "Last-Translator: Phumraphee Sae-tang \n" "Language-Team: Thai \n" "Language-Team: Turkish \n" "Language-Team: Ukrainian {text}" #~ msgstr "" @@ -37514,9 +37711,6 @@ msgstr "Косово" #~ msgid "Please do not use special characters in names." #~ msgstr "Будь ласка, не використовуйте спеціальні символи в іменах." -#~ msgid "State" -#~ msgstr "Штат" - #, fuzzy #~| msgid "Printing date and time" #~ msgid "Program times: date and time" diff --git a/src/pretix/locale/vi/LC_MESSAGES/django.po b/src/pretix/locale/vi/LC_MESSAGES/django.po index 3410b2053..3a5c82350 100644 --- a/src/pretix/locale/vi/LC_MESSAGES/django.po +++ b/src/pretix/locale/vi/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-30 13:52+0000\n" +"POT-Creation-Date: 2026-08-24 14:39+0000\n" "PO-Revision-Date: 2025-06-11 02:00+0000\n" "Last-Translator: Michael Dao \n" "Language-Team: Vietnamese \n" "Language-Team: Chinese (Simplified Han script) \n" "Language-Team: Chinese (Traditional Han script) Date: Mon, 24 Aug 2026 17:12:24 +0200 Subject: [PATCH 081/249] Update po files [CI skip] Signed-off-by: Raphael Michel --- src/pretix/locale/django.pot | 2 +- src/pretix/locale/djangojs.pot | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pretix/locale/django.pot b/src/pretix/locale/django.pot index ea96cf984..08c40e848 100644 --- a/src/pretix/locale/django.pot +++ b/src/pretix/locale/django.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-24 14:39+0000\n" +"POT-Creation-Date: 2026-08-24 15:12+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/src/pretix/locale/djangojs.pot b/src/pretix/locale/djangojs.pot index fcf88cf7d..9c6c4fd49 100644 --- a/src/pretix/locale/djangojs.pot +++ b/src/pretix/locale/djangojs.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-24 14:39+0000\n" +"POT-Creation-Date: 2026-08-24 15:12+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" -- 2.54.0 From b74371bef190e21501903006fbf047dccd7e3e51 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 24 Aug 2026 17:26:37 +0200 Subject: [PATCH 082/249] Fix typos --- src/pretix/base/reldate.py | 4 ++-- src/pretix/plugins/paypal2/payment.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pretix/base/reldate.py b/src/pretix/base/reldate.py index 510a4b466..fc89643d9 100644 --- a/src/pretix/base/reldate.py +++ b/src/pretix/base/reldate.py @@ -742,9 +742,9 @@ class RelativeDateField(RelativeDateTimeField): if data.rel_days_number is None or not data.rel_days_relationto: raise ValidationError(self.error_messages['incomplete']) elif data.rel_days_relation == "before" and not choice.supports_before: - raise ValidationError(_("A relative date cannot be expressed as 'before' for '{}'".format(choice.text))) + raise ValidationError(_('A relative date cannot be expressed as "before" for "{}"'.format(choice.text))) elif data.rel_days_relation == "after" and not choice.supports_after: - raise ValidationError(_("A relative date cannot be expressed as 'after' for '{}'".format(choice.text))) + raise ValidationError(_('A relative date cannot be expressed as "after" for "{}"'.format(choice.text))) return forms.MultiValueField.clean(self, value) diff --git a/src/pretix/plugins/paypal2/payment.py b/src/pretix/plugins/paypal2/payment.py index 019bb2d98..d89b8dada 100644 --- a/src/pretix/plugins/paypal2/payment.py +++ b/src/pretix/plugins/paypal2/payment.py @@ -199,7 +199,7 @@ class PaypalSettingsHolder(BasePaymentProvider): help_text=_( 'PayPals fraud prevention might block processing of individual payments for a considerable amount ' 'of time. The payment is marked as "pending" during this time window. You can allow your customers to ' - 'start another payment attempts during that window. This might result in them being charged twice if the' + 'start another payment attempts during that window. This might result in them being charged twice if the ' 'original payment is approved.' ), required=False -- 2.54.0 From 2af60b499fa7cedd6410548e5f2eb3dcf2342f09 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 24 Aug 2026 17:26:37 +0200 Subject: [PATCH 083/249] Fix typos and extend wordlist --- src/pretix/locale/de/wordlist.txt | 2 ++ src/pretix/locale/de_Informal/wordlist.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/pretix/locale/de/wordlist.txt b/src/pretix/locale/de/wordlist.txt index 4d0ea3f0e..554b0dba7 100644 --- a/src/pretix/locale/de/wordlist.txt +++ b/src/pretix/locale/de/wordlist.txt @@ -293,6 +293,7 @@ Pago parsen Pay PayPal +PayPals PayPal-Account PayPal-Accounts PayPal-Konto @@ -340,6 +341,7 @@ pretix-Kontos pretix-Logo pretix-Logos pretix-Plugins +pretixPOS pretixPRINT pretixPRINT-Version pretixSCAN diff --git a/src/pretix/locale/de_Informal/wordlist.txt b/src/pretix/locale/de_Informal/wordlist.txt index 4d0ea3f0e..554b0dba7 100644 --- a/src/pretix/locale/de_Informal/wordlist.txt +++ b/src/pretix/locale/de_Informal/wordlist.txt @@ -293,6 +293,7 @@ Pago parsen Pay PayPal +PayPals PayPal-Account PayPal-Accounts PayPal-Konto @@ -340,6 +341,7 @@ pretix-Kontos pretix-Logo pretix-Logos pretix-Plugins +pretixPOS pretixPRINT pretixPRINT-Version pretixSCAN -- 2.54.0 From 3fbd63e80a90d5735b3fb4aca307d2bca056026f Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 24 Aug 2026 17:30:32 +0200 Subject: [PATCH 084/249] Translations: Update German Currently translated at 100.0% (6421 of 6421 strings) Translation: pretix/pretix Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/ powered by weblate --- src/pretix/locale/de/LC_MESSAGES/django.po | 129 +++++++++------------ 1 file changed, 58 insertions(+), 71 deletions(-) diff --git a/src/pretix/locale/de/LC_MESSAGES/django.po b/src/pretix/locale/de/LC_MESSAGES/django.po index 0b9d9d821..31b1c907b 100644 --- a/src/pretix/locale/de/LC_MESSAGES/django.po +++ b/src/pretix/locale/de/LC_MESSAGES/django.po @@ -5,16 +5,16 @@ msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-24 14:39+0000\n" -"PO-Revision-Date: 2026-07-30 18:31+0000\n" +"PO-Revision-Date: 2026-08-24 15:31+0000\n" "Last-Translator: Raphael Michel \n" -"Language-Team: German \n" +"Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "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.7.1\n" +"X-Generator: Weblate 2026.8.1\n" "X-Poedit-Bookmarks: -1,-1,904,-1,-1,-1,-1,-1,-1,-1\n" #: pretix/_base_settings.py @@ -91,7 +91,7 @@ msgstr "Hebräisch" #: pretix/_base_settings.py msgid "Hungarian" -msgstr "" +msgstr "Ungarisch" #: pretix/_base_settings.py msgid "Indonesian" @@ -434,10 +434,8 @@ msgid "You cannot exchange a medium for a medium." msgstr "Sie können kein Medium gegen ein Medium eintauschen." #: pretix/api/views/checkin.py -#, fuzzy -#| msgid "Product does not support medium exchange." msgid "You cannot simulate a medium exchange." -msgstr "Das Produkt erlaubt keinen Umtausch in ein Medium." +msgstr "Ein Medientausch kann nicht simuliert werden." #: pretix/api/views/oauth.py pretix/control/logdisplay.py #, python-brace-format @@ -5800,11 +5798,8 @@ msgid "Country code (ISO 3166-1 alpha-2)" msgstr "Land (ISO3166-1-alpha-2-Ländercode)" #: pretix/base/models/items.py -#, fuzzy -#| msgctxt "attendee_data" -#| msgid "Asked" msgid "Asked on" -msgstr "Abfragen" +msgstr "Gefragt bei" #: pretix/base/models/items.py pretix/base/models/organizer.py msgid "" @@ -7506,9 +7501,6 @@ msgid "The payment for this invoice has already been received." msgstr "Die Rechnung wurde bereits beglichen." #: pretix/base/payment.py -#, fuzzy -#| msgid "" -#| "This payment is already being processed and can not be canceled any more." msgid "" "This payment is already being processed and cannot be canceled any more." msgstr "" @@ -8290,16 +8282,12 @@ msgid "Presale end" msgstr "Ende des Vorverkaufs" #: pretix/base/reldate.py -#, fuzzy -#| msgid "Order email" msgid "Order creation" -msgstr "E-Mail-Adresse der Bestellung" +msgstr "Bestelldatum" #: pretix/base/reldate.py -#, fuzzy -#| msgid "Order expired" msgid "Order expiry" -msgstr "Bestellung abgelaufen" +msgstr "Zahlungsfrist" #: pretix/base/reldate.py msgid "before" @@ -8328,32 +8316,32 @@ msgstr "Nicht gesetzt" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as \"before\" for \"{}\"" -msgstr "" +msgstr "Ein relatives Datum kann nicht als \"vor {}\" ausgedrückt werden" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as \"after\" for \"{}\"" -msgstr "" +msgstr "Ein relatives Datum kann nicht als \"nach {}\" ausgedrückt werden" #: pretix/base/reldate.py #, python-brace-format msgid "A relative time cannot be expressed as \"before\" for \"{}\"" -msgstr "" +msgstr "Ein relativer Zeitpunkt kann nicht als \"vor {}\" ausgedrückt werden" #: pretix/base/reldate.py #, python-brace-format msgid "A relative time cannot be expressed as \"after\" for \"{}\"" -msgstr "" +msgstr "Ein relativer Zeitpunkt kann nicht als \"nach {}\" ausgedrückt werden" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as 'before' for '{}'" -msgstr "" +msgstr "Ein relatives Datum kann nicht als \"vor {}\" ausgedrückt werden" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as 'after' for '{}'" -msgstr "" +msgstr "Ein relatives Datum kann nicht als \"nach {}\" ausgedrückt werden" #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" @@ -10372,12 +10360,12 @@ msgstr "" #: pretix/base/settings.py msgid "No dates match your criteria." -msgstr "" +msgstr "Keine Termine passen zu Ihren Kriterien." #: pretix/base/settings.py msgctxt "subevents" msgid "Text for empty date results" -msgstr "" +msgstr "Text für leere Terminanzeige" #: pretix/base/settings.py msgctxt "subevents" @@ -10388,6 +10376,11 @@ msgid "" "touch with you to arrange further dates. We do not recommend more than one " "or two sentences." msgstr "" +"Dieser Text wird im Kalender oder der Terminliste angezeigt, wenn sie leer " +"ist, weil z.B. der Monat keine Termine enthält oder der vom Nutzer gesetzte " +"Filter keine Treffer hat. Sie können hier Wege bewerben, wie man Kontakt für " +"zusätzliche Termine aufnehmen kann. Wir empfehlen nicht mehr als ein oder " +"zwei Sätze." #: pretix/base/settings.py msgid "Guidance text" @@ -14371,28 +14364,20 @@ msgstr "" "Ausstellung ablaufen." #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment date" msgid "Payment term" -msgstr "Zahlungsdatum" +msgstr "Zahlungsfrist" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "(Same as above)" msgid "same as above" -msgstr "(wie oben)" +msgstr "wie oben" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment term in days" msgid "different payment term in days" -msgstr "Zahlungsziel in Tagen" +msgstr "abweichendes Zahlungsziel in Tagen" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment term in minutes" msgid "different payment term in minutes" -msgstr "Zahlungsziel in Minuten" +msgstr "abweichendes Zahlungsziel in Minuten" #: pretix/control/forms/event.py msgid "Prices including tax" @@ -23224,28 +23209,24 @@ msgstr "" "z.B. nach Allergien Ihrer Teilnehmer fragen." #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Create a new question" msgid "Create a new per-ticket question" -msgstr "Neue Frage erstellen" +msgstr "Neue Frage für jedes Ticket erstellen" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Create a new question" msgid "Create a new order-level question" -msgstr "Neue Frage erstellen" +msgstr "Neue Frage für jede Bestellung erstellen" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Delete question" msgid "Per-ticket questions" -msgstr "Frage löschen" +msgstr "Fragen pro Ticket" #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "These questions are asked for every ticket, so possibly multiple times in " "the same order." msgstr "" +"Diese Fragen werden für jedes Ticket abgefragt, also ggf. mehrfach pro " +"Bestellung." #: pretix/control/templates/pretixcontrol/items/questions.html msgid "Create a new question" @@ -23264,28 +23245,28 @@ msgid "All personalized products" msgstr "Alle personalisierten Produkte" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Include questions" msgid "Per-order questions" -msgstr "Fragen anzeigen" +msgstr "Fragen pro Bestellung" #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "This functionality is in active development and expected to change " "significantly over the coming months." msgstr "" +"Diese Funktion befindet sich in aktiver Entwicklung und wird sich über die " +"nächsten Monate stark verändern." #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "Per-order questions are currently not supported and will not be displayed in " "pretixPOS." msgstr "" +"Fragen pro Bestellung werden derzeit von pretixPOS weder für Anzeige noch " +"Abfrage unterstützt." #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "The question has been reordered." msgid "These questions are asked once per order." -msgstr "Die Frage wurde umsortiert." +msgstr "Diese Fragen werden einmal pro Bestellung gestellt." #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html @@ -23895,10 +23876,8 @@ msgstr "(optional)" #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/checkout_questions.html #: pretix/presale/templates/pretixpresale/event/order_modify.html -#, fuzzy -#| msgid "Additional information" msgid "Additional order information" -msgstr "Zusätzliche Informationen" +msgstr "Zusätzliche Bestellinformationen" #: pretix/control/templates/pretixcontrol/order/delete.html msgid "Delete order" @@ -25608,14 +25587,13 @@ msgid "Hardware model" msgstr "Hardware-Modell" #: pretix/control/templates/pretixcontrol/organizers/devices.html -#, fuzzy, python-format -#| msgid "Begin: %(time)s" +#, python-format msgid "Last seen: %(time)s" -msgstr "Beginn: %(time)s Uhr" +msgstr "Zuletzt gesehen: %(time)s Uhr" #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "No recent contact" -msgstr "" +msgstr "Kein kürzlicher Kontakt" #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "Not yet initialized" @@ -29085,17 +29063,12 @@ msgstr "" "Einstellungen Ihrer Domain anpassen." #: pretix/control/views/mailsetup.py -#, fuzzy -#| msgid "" -#| "We did not find DMARC record for your domain. This means that there is a " -#| "very high chance most of the emails will be rejected or marked as spam. " -#| "You should update the DNS settings of your domain." msgid "" "We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" -"Wir haben keinen DMARC-Eintrag für die Domain gefunden Es gibt daher eine " +"Wir haben keinen DMARC-Eintrag für die Domain gefunden. Es gibt daher eine " "hohe Chance, dass die E-Mails abgelehnt werden oder im Spam landen. Sie " "sollten die DNS-Einstellungen Ihrer Domain anpassen." @@ -31839,7 +31812,7 @@ msgstr "" #: pretix/plugins/paypal2/payment.py msgid "Allow further payments during compliance hold" -msgstr "" +msgstr "Erlaube weitere Zahlungsversuche während PayPal eine Zahlung überprüft" #: pretix/plugins/paypal2/payment.py msgid "" @@ -31849,16 +31822,24 @@ msgid "" "attempts during that window. This might result in them being charged twice " "if theoriginal payment is approved." msgstr "" +"PayPals Betrugserkennung blockiert die Verarbeitung von Zahlungen in " +"Einzelfällen für signifikante Zeiträume. Die Zahlung wird in diesem Zeitraum " +"als \"ausstehend\" angezeigt. Sie können Ihren Kunden erlauben, in diesem " +"Zeitfenster weitere Zahlungsversuche zu starten. Dies kann dazu führen, dass " +"mehrere Zahlungen stattfinden, wenn der ursprüngliche Versuch freigegeben " +"wird." #: pretix/plugins/paypal2/payment.py msgid "Timeout further payment attempts" -msgstr "" +msgstr "Zeitlimit für weitere Zahlungsversuche" #: pretix/plugins/paypal2/payment.py msgid "" "Time duration in minutes after which another payment attempt is possible, " "while the last payment is still under investigation." msgstr "" +"Zeit in Minuten nach denen ein weitere Zahlungsversuch möglich ist während " +"der vorherige noch untersucht wird." #: pretix/plugins/paypal2/payment.py msgid "-- Automatic --" @@ -32136,6 +32117,12 @@ msgid "" "twice in case PayPal allows your initial payment attempt. Please contact us " "to resolve this case." msgstr "" +"Ihre Zahlung wird von PayPal verarbeitet. Dies braucht länger als üblich. " +"Sie können warten, bis PayPal die Zahlung bestätigt oder Sie können die " +"Zahlung erneut mit dieser oder einer anderen Zahlungsmethode versuchen. Dies " +"kann dazu führen, dass Ihnen der Betrag doppelt abgebucht wird, wenn PayPal " +"den ursprünglichen Versuch bestätigt. Bitte kontaktieren Sie uns in diesem " +"Fall." #: pretix/plugins/paypal2/views.py msgid "" -- 2.54.0 From bf85789efd17743d15adb941e84491d0610206bf Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 24 Aug 2026 17:31:05 +0200 Subject: [PATCH 085/249] Translations: Update German (informal) (de_Informal) Currently translated at 100.0% (6421 of 6421 strings) Translation: pretix/pretix Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/ powered by weblate --- .../locale/de_Informal/LC_MESSAGES/django.po | 126 ++++++++---------- 1 file changed, 56 insertions(+), 70 deletions(-) diff --git a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po index a0844d79e..7d519d48c 100644 --- a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po +++ b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-24 14:39+0000\n" -"PO-Revision-Date: 2026-07-30 18:31+0000\n" +"PO-Revision-Date: 2026-08-24 15:31+0000\n" "Last-Translator: Raphael Michel \n" "Language-Team: German (informal) \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.7.1\n" +"X-Generator: Weblate 2026.8.1\n" #: pretix/_base_settings.py msgid "English" @@ -93,7 +93,7 @@ msgstr "Hebräisch" #: pretix/_base_settings.py msgid "Hungarian" -msgstr "" +msgstr "Ungarisch" #: pretix/_base_settings.py msgid "Indonesian" @@ -436,10 +436,8 @@ msgid "You cannot exchange a medium for a medium." msgstr "Du kannst kein Medium gegen ein Medium eintauschen." #: pretix/api/views/checkin.py -#, fuzzy -#| msgid "Product does not support medium exchange." msgid "You cannot simulate a medium exchange." -msgstr "Das Produkt erlaubt keinen Umtausch in ein Medium." +msgstr "Ein Medientausch kann nicht simuliert werden." #: pretix/api/views/oauth.py pretix/control/logdisplay.py #, python-brace-format @@ -5797,11 +5795,8 @@ msgid "Country code (ISO 3166-1 alpha-2)" msgstr "Land (ISO3166-1-alpha-2-Ländercode)" #: pretix/base/models/items.py -#, fuzzy -#| msgctxt "attendee_data" -#| msgid "Asked" msgid "Asked on" -msgstr "Abfragen" +msgstr "Gefragt bei" #: pretix/base/models/items.py pretix/base/models/organizer.py msgid "" @@ -7499,9 +7494,6 @@ msgid "The payment for this invoice has already been received." msgstr "Die Rechnung wurde bereits beglichen." #: pretix/base/payment.py -#, fuzzy -#| msgid "" -#| "This payment is already being processed and can not be canceled any more." msgid "" "This payment is already being processed and cannot be canceled any more." msgstr "" @@ -8283,16 +8275,12 @@ msgid "Presale end" msgstr "Ende des Vorverkaufs" #: pretix/base/reldate.py -#, fuzzy -#| msgid "Order email" msgid "Order creation" -msgstr "E-Mail-Adresse der Bestellung" +msgstr "Bestelldatum" #: pretix/base/reldate.py -#, fuzzy -#| msgid "Order expired" msgid "Order expiry" -msgstr "Bestellung abgelaufen" +msgstr "Zahlungsfrist" #: pretix/base/reldate.py msgid "before" @@ -8321,32 +8309,32 @@ msgstr "Nicht gesetzt" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as \"before\" for \"{}\"" -msgstr "" +msgstr "Ein relatives Datum kann nicht als \"vor {}\" ausgedrückt werden" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as \"after\" for \"{}\"" -msgstr "" +msgstr "Ein relatives Datum kann nicht als \"nach {}\" ausgedrückt werden" #: pretix/base/reldate.py #, python-brace-format msgid "A relative time cannot be expressed as \"before\" for \"{}\"" -msgstr "" +msgstr "Ein relativer Zeitpunkt kann nicht als \"vor {}\" ausgedrückt werden" #: pretix/base/reldate.py #, python-brace-format msgid "A relative time cannot be expressed as \"after\" for \"{}\"" -msgstr "" +msgstr "Ein relativer Zeitpunkt kann nicht als \"nach {}\" ausgedrückt werden" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as 'before' for '{}'" -msgstr "" +msgstr "Ein relatives Datum kann nicht als \"vor {}\" ausgedrückt werden" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as 'after' for '{}'" -msgstr "" +msgstr "Ein relatives Datum kann nicht als \"nach {}\" ausgedrückt werden" #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" @@ -10360,12 +10348,12 @@ msgstr "" #: pretix/base/settings.py msgid "No dates match your criteria." -msgstr "" +msgstr "Keine Termine passen zu deinen Kriterien." #: pretix/base/settings.py msgctxt "subevents" msgid "Text for empty date results" -msgstr "" +msgstr "Text für leere Terminanzeige" #: pretix/base/settings.py msgctxt "subevents" @@ -10376,6 +10364,11 @@ msgid "" "touch with you to arrange further dates. We do not recommend more than one " "or two sentences." msgstr "" +"Dieser Text wird im Kalender oder der Terminliste angezeigt, wenn sie leer " +"ist, weil z.B. der Monat keine Termine enthält oder der vom Nutzer gesetzte " +"Filter keine Treffer hat. Du kannst hier Wege bewerben, wie man Kontakt für " +"zusätzliche Termine aufnehmen kann. Wir empfehlen nicht mehr als ein oder " +"zwei Sätze." #: pretix/base/settings.py msgid "Guidance text" @@ -14347,28 +14340,20 @@ msgstr "" "Ausstellung ablaufen." #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment date" msgid "Payment term" -msgstr "Zahlungsdatum" +msgstr "Zahlungsfrist" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "(Same as above)" msgid "same as above" -msgstr "(wie oben)" +msgstr "wie oben" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment term in days" msgid "different payment term in days" -msgstr "Zahlungsziel in Tagen" +msgstr "abweichendes Zahlungsziel in Tagen" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment term in minutes" msgid "different payment term in minutes" -msgstr "Zahlungsziel in Minuten" +msgstr "abweichendes Zahlungsziel in Minuten" #: pretix/control/forms/event.py msgid "Prices including tax" @@ -23194,28 +23179,24 @@ msgstr "" "könntest du z.B. nach Allergien deiner Teilnehmer*innen fragen." #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Create a new question" msgid "Create a new per-ticket question" -msgstr "Neue Frage erstellen" +msgstr "Neue Frage für jedes Ticket erstellen" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Create a new question" msgid "Create a new order-level question" -msgstr "Neue Frage erstellen" +msgstr "Neue Frage für jede Bestellung erstellen" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Delete question" msgid "Per-ticket questions" -msgstr "Frage löschen" +msgstr "Fragen pro Ticket" #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "These questions are asked for every ticket, so possibly multiple times in " "the same order." msgstr "" +"Diese Fragen werden für jedes Ticket abgefragt, also ggf. mehrfach pro " +"Bestellung." #: pretix/control/templates/pretixcontrol/items/questions.html msgid "Create a new question" @@ -23234,28 +23215,28 @@ msgid "All personalized products" msgstr "Alle personalisierten Produkte" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Include questions" msgid "Per-order questions" -msgstr "Fragen anzeigen" +msgstr "Fragen pro Bestellung" #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "This functionality is in active development and expected to change " "significantly over the coming months." msgstr "" +"Diese Funktion befindet sich in aktiver Entwicklung und wird sich über die " +"nächsten Monate stark verändern." #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "Per-order questions are currently not supported and will not be displayed in " "pretixPOS." msgstr "" +"Fragen pro Bestellung werden derzeit von pretixPOS weder für Anzeige noch " +"Abfrage unterstützt." #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "The question has been reordered." msgid "These questions are asked once per order." -msgstr "Die Frage wurde umsortiert." +msgstr "Diese Fragen werden einmal pro Bestellung gestellt." #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html @@ -23865,10 +23846,8 @@ msgstr "(optional)" #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/checkout_questions.html #: pretix/presale/templates/pretixpresale/event/order_modify.html -#, fuzzy -#| msgid "Additional information" msgid "Additional order information" -msgstr "Zusätzliche Informationen" +msgstr "Zusätzliche Bestellinformationen" #: pretix/control/templates/pretixcontrol/order/delete.html msgid "Delete order" @@ -25574,14 +25553,13 @@ msgid "Hardware model" msgstr "Hardware-Modell" #: pretix/control/templates/pretixcontrol/organizers/devices.html -#, fuzzy, python-format -#| msgid "Begin: %(time)s" +#, python-format msgid "Last seen: %(time)s" -msgstr "Beginn: %(time)s Uhr" +msgstr "Zuletzt gesehen: %(time)s Uhr" #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "No recent contact" -msgstr "" +msgstr "Kein kürzlicher Kontakt" #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "Not yet initialized" @@ -29044,19 +29022,14 @@ msgstr "" "Einstellungen deiner Domain anpassen." #: pretix/control/views/mailsetup.py -#, fuzzy -#| msgid "" -#| "We did not find DMARC record for your domain. This means that there is a " -#| "very high chance most of the emails will be rejected or marked as spam. " -#| "You should update the DNS settings of your domain." msgid "" "We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" -"Wir haben keinen DMARC-Eintrag für die Domain gefunden Es gibt daher eine " +"Wir haben keinen DMARC-Eintrag für die Domain gefunden. Es gibt daher eine " "hohe Chance, dass die E-Mails abgelehnt werden oder im Spam landen. Du " -"solltest die DNS-Einstellungen deiner Domain anpassen." +"solltest die DNS-Einstellungen Ihrer Domain anpassen." #: pretix/control/views/mailsetup.py msgid "The verification code was incorrect, please try again." @@ -31795,7 +31768,7 @@ msgstr "" #: pretix/plugins/paypal2/payment.py msgid "Allow further payments during compliance hold" -msgstr "" +msgstr "Erlaube weitere Zahlungsversuche während PayPal eine Zahlung überprüft" #: pretix/plugins/paypal2/payment.py msgid "" @@ -31805,16 +31778,24 @@ msgid "" "attempts during that window. This might result in them being charged twice " "if theoriginal payment is approved." msgstr "" +"PayPals Betrugserkennung blockiert die Verarbeitung von Zahlungen in " +"Einzelfällen für signifikante Zeiträume. Die Zahlung wird in diesem Zeitraum " +"als \"ausstehend\" angezeigt. Du kannst deinen Kunden erlauben, in diesem " +"Zeitfenster weitere Zahlungsversuche zu starten. Dies kann dazu führen, dass " +"mehrere Zahlungen stattfinden, wenn der ursprüngliche Versuch freigegeben " +"wird." #: pretix/plugins/paypal2/payment.py msgid "Timeout further payment attempts" -msgstr "" +msgstr "Zeitlimit für weitere Zahlungsversuche" #: pretix/plugins/paypal2/payment.py msgid "" "Time duration in minutes after which another payment attempt is possible, " "while the last payment is still under investigation." msgstr "" +"Zeit in Minuten nach denen ein weitere Zahlungsversuch möglich ist während " +"der vorherige noch untersucht wird." #: pretix/plugins/paypal2/payment.py msgid "-- Automatic --" @@ -32090,6 +32071,11 @@ msgid "" "twice in case PayPal allows your initial payment attempt. Please contact us " "to resolve this case." msgstr "" +"Deine Zahlung wird von PayPal verarbeitet. Dies braucht länger als üblich. " +"Du kannst warten, bis PayPal die Zahlung bestätigt oder du kannst die " +"Zahlung erneut mit dieser oder einer anderen Zahlungsmethode versuchen. Dies " +"kann dazu führen, dass dir der Betrag doppelt abgebucht wird, wenn PayPal " +"den ursprünglichen Versuch bestätigt. Bitte kontaktiere uns in diesem Fall." #: pretix/plugins/paypal2/views.py msgid "" -- 2.54.0 From f476ce769653d8ec6930ddf64459828af35bd5bb Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 24 Aug 2026 17:36:00 +0200 Subject: [PATCH 086/249] Update po files [CI skip] Signed-off-by: Raphael Michel --- src/pretix/locale/af/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/ang/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/ar/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/az/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/ca/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/cs/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/cy/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/da/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/de/LC_MESSAGES/django.po | 33 +++++++++++-------- .../locale/de_Informal/LC_MESSAGES/django.po | 29 +++++++++------- src/pretix/locale/django.pot | 14 ++------ src/pretix/locale/djangojs.pot | 2 +- src/pretix/locale/el/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/enm/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/es/LC_MESSAGES/django.po | 14 ++------ .../locale/es_419/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/et/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/eu/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/fi/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/fo/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/fr/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/gl/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/he/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/hr/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/hu/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/id/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/it/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/ja/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/ko/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/lb/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/lt/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/lv/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/nan/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/nb_NO/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/nl/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/nl_BE/LC_MESSAGES/django.po | 14 ++------ .../locale/nl_Informal/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/pl/LC_MESSAGES/django.po | 14 ++------ .../locale/pl_Informal/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/pt/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/pt_BR/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/pt_PT/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/ro/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/ru/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/si/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/sk/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/sl/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/sq/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/sv/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/th/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/tr/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/uk/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/vi/LC_MESSAGES/django.po | 14 ++------ src/pretix/locale/vls/LC_MESSAGES/django.po | 14 ++------ .../locale/zh_Hans/LC_MESSAGES/django.po | 14 ++------ .../locale/zh_Hant/LC_MESSAGES/django.po | 14 ++------ 56 files changed, 143 insertions(+), 663 deletions(-) diff --git a/src/pretix/locale/af/LC_MESSAGES/django.po b/src/pretix/locale/af/LC_MESSAGES/django.po index f55515c0e..1319c3a2d 100644 --- a/src/pretix/locale/af/LC_MESSAGES/django.po +++ b/src/pretix/locale/af/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-24 14:39+0000\n" +"POT-Creation-Date: 2026-08-24 15:35+0000\n" "PO-Revision-Date: 2026-07-15 14:20+0000\n" "Last-Translator: Llewelyn Barkhuizen \n" "Language-Team: Afrikaans \n" "Language-Team: Arabic \n" "Language-Team: Azerbaijani \n" "Language-Team: Catalan \n" "Language-Team: Czech \n" "Language-Team: Welsh \n" "Language-Team: Danish \n" -"Language-Team: German \n" +"Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -8333,16 +8333,6 @@ msgstr "Ein relativer Zeitpunkt kann nicht als \"vor {}\" ausgedrückt werden" msgid "A relative time cannot be expressed as \"after\" for \"{}\"" msgstr "Ein relativer Zeitpunkt kann nicht als \"nach {}\" ausgedrückt werden" -#: pretix/base/reldate.py -#, python-brace-format -msgid "A relative date cannot be expressed as 'before' for '{}'" -msgstr "Ein relatives Datum kann nicht als \"vor {}\" ausgedrückt werden" - -#: pretix/base/reldate.py -#, python-brace-format -msgid "A relative date cannot be expressed as 'after' for '{}'" -msgstr "Ein relatives Datum kann nicht als \"nach {}\" ausgedrückt werden" - #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" msgstr "Zufällig (Standard, funktioniert mit allen pretix-Apps)" @@ -31815,12 +31805,19 @@ msgid "Allow further payments during compliance hold" msgstr "Erlaube weitere Zahlungsversuche während PayPal eine Zahlung überprüft" #: pretix/plugins/paypal2/payment.py +#, fuzzy +#| msgid "" +#| "PayPals fraud prevention might block processing of individual payments " +#| "for a considerable amount of time. The payment is marked as \"pending\" " +#| "during this time window. You can allow your customers to start another " +#| "payment attempts during that window. This might result in them being " +#| "charged twice if theoriginal payment is approved." msgid "" "PayPals fraud prevention might block processing of individual payments for a " "considerable amount of time. The payment is marked as \"pending\" during " "this time window. You can allow your customers to start another payment " "attempts during that window. This might result in them being charged twice " -"if theoriginal payment is approved." +"if the original payment is approved." msgstr "" "PayPals Betrugserkennung blockiert die Verarbeitung von Zahlungen in " "Einzelfällen für signifikante Zeiträume. Die Zahlung wird in diesem Zeitraum " @@ -37427,6 +37424,14 @@ msgstr "Schreibzugriff" msgid "Kosovo" msgstr "Kosovo" +#, python-brace-format +#~ msgid "A relative date cannot be expressed as 'before' for '{}'" +#~ msgstr "Ein relatives Datum kann nicht als \"vor {}\" ausgedrückt werden" + +#, python-brace-format +#~ msgid "A relative date cannot be expressed as 'after' for '{}'" +#~ msgstr "Ein relatives Datum kann nicht als \"nach {}\" ausgedrückt werden" + #~ msgid "" #~ "Our attempt to execute your payment via PayPal has failed. Please try " #~ "again or contact us." diff --git a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po index 7d519d48c..7435bef1c 100644 --- a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po +++ b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-24 14:39+0000\n" +"POT-Creation-Date: 2026-08-24 15:35+0000\n" "PO-Revision-Date: 2026-08-24 15:31+0000\n" "Last-Translator: Raphael Michel \n" "Language-Team: German (informal) \n" "Language-Team: LANGUAGE \n" @@ -7661,16 +7661,6 @@ msgstr "" msgid "A relative time cannot be expressed as \"after\" for \"{}\"" msgstr "" -#: pretix/base/reldate.py -#, python-brace-format -msgid "A relative date cannot be expressed as 'before' for '{}'" -msgstr "" - -#: pretix/base/reldate.py -#, python-brace-format -msgid "A relative date cannot be expressed as 'after' for '{}'" -msgstr "" - #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" msgstr "" @@ -27910,7 +27900,7 @@ msgid "" "considerable amount of time. The payment is marked as \"pending\" during " "this time window. You can allow your customers to start another payment " "attempts during that window. This might result in them being charged twice " -"if theoriginal payment is approved." +"if the original payment is approved." msgstr "" #: pretix/plugins/paypal2/payment.py diff --git a/src/pretix/locale/djangojs.pot b/src/pretix/locale/djangojs.pot index 9c6c4fd49..136f777f3 100644 --- a/src/pretix/locale/djangojs.pot +++ b/src/pretix/locale/djangojs.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-24 15:12+0000\n" +"POT-Creation-Date: 2026-08-24 15:35+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/src/pretix/locale/el/LC_MESSAGES/django.po b/src/pretix/locale/el/LC_MESSAGES/django.po index 0a762a0a1..47564a1f7 100644 --- a/src/pretix/locale/el/LC_MESSAGES/django.po +++ b/src/pretix/locale/el/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-24 14:39+0000\n" +"POT-Creation-Date: 2026-08-24 15:35+0000\n" "PO-Revision-Date: 2026-02-25 23:00+0000\n" "Last-Translator: David Ibáñez Cerdeira \n" "Language-Team: Greek \n" "Language-Team: Spanish \n" "Language-Team: Spanish (Latin America) \n" "Language-Team: Estonian \n" "Language-Team: Basque \n" "Language-Team: Finnish \n" "Language-Team: Faroese \n" "Language-Team: French \n" "Language-Team: Galician \n" "Language-Team: Hebrew \n" "Language-Team: Croatian \n" "Language-Team: Hungarian \n" "Language-Team: Indonesian \n" @@ -8342,16 +8342,6 @@ msgstr "" msgid "A relative time cannot be expressed as \"after\" for \"{}\"" msgstr "" -#: pretix/base/reldate.py -#, python-brace-format -msgid "A relative date cannot be expressed as 'before' for '{}'" -msgstr "" - -#: pretix/base/reldate.py -#, python-brace-format -msgid "A relative date cannot be expressed as 'after' for '{}'" -msgstr "" - #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" msgstr "Casuale (predefinito, funziona con tutte le app pretix)" @@ -33468,7 +33458,7 @@ msgid "" "considerable amount of time. The payment is marked as \"pending\" during " "this time window. You can allow your customers to start another payment " "attempts during that window. This might result in them being charged twice " -"if theoriginal payment is approved." +"if the original payment is approved." msgstr "" #: pretix/plugins/paypal2/payment.py diff --git a/src/pretix/locale/ja/LC_MESSAGES/django.po b/src/pretix/locale/ja/LC_MESSAGES/django.po index ad348c57f..2889e5b74 100644 --- a/src/pretix/locale/ja/LC_MESSAGES/django.po +++ b/src/pretix/locale/ja/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-08-24 14:39+0000\n" +"POT-Creation-Date: 2026-08-24 15:35+0000\n" "PO-Revision-Date: 2026-08-16 22:00+0000\n" "Last-Translator: Hijiri Umemoto \n" "Language-Team: Japanese \n" "Language-Team: Korean \n" "Language-Team: Luxembourgish \n" "Language-Team: Latvian \n" "Language-Team: Norwegian Bokmål \n" "Language-Team: Dutch \n" "Language-Team: Dutch (Belgium) \n" "Language-Team: Dutch (informal) \n" "Language-Team: Polish \n" "Language-Team: Polish (informal) \n" "Language-Team: Portuguese \n" "Language-Team: Portuguese (Brazil) \n" "Language-Team: Portuguese (Portugal) \n" "Language-Team: Romanian \n" "Language-Team: Russian \n" "Language-Team: Sinhala \n" "Language-Team: Slovak \n" "Language-Team: Slovenian \n" "Language-Team: Albanian \n" "Language-Team: Swedish \n" "Language-Team: Thai \n" "Language-Team: Turkish \n" "Language-Team: Ukrainian \n" "Language-Team: Vietnamese \n" "Language-Team: Chinese (Simplified Han script) \n" "Language-Team: Chinese (Traditional Han script) Date: Mon, 24 Aug 2026 17:49:33 +0200 Subject: [PATCH 087/249] Translations: Update word list --- src/pretix/locale/wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pretix/locale/wordlist.txt b/src/pretix/locale/wordlist.txt index dd8182799..de0d991c6 100644 --- a/src/pretix/locale/wordlist.txt +++ b/src/pretix/locale/wordlist.txt @@ -117,6 +117,7 @@ OpenStreetMap overpayment OXXO Pago +PayPals PayU Peppol PCI -- 2.54.0 From 6d4aba6e3d92a1bcec05f09a0e34d65aa32e1b68 Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Tue, 25 Aug 2026 09:37:37 +0200 Subject: [PATCH 088/249] Questions: add min-length to string/text type questions (#6488) * Questions: add min-length to string/text type questions * fix test --- doc/api/resources/questions.rst | 5 +++++ src/pretix/api/serializers/item.py | 2 +- src/pretix/base/forms/questions.py | 2 ++ .../0310_question_valid_string_length_min.py | 19 +++++++++++++++++++ src/pretix/base/models/items.py | 14 +++++++++++++- src/pretix/control/forms/item.py | 1 + .../pretixcontrol/items/question_edit.html | 1 + src/tests/api/test_items.py | 1 + src/tests/base/test_models.py | 6 ++++-- 9 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 src/pretix/base/migrations/0310_question_valid_string_length_min.py diff --git a/doc/api/resources/questions.rst b/doc/api/resources/questions.rst index 5cc5fa32f..4f10d4984 100644 --- a/doc/api/resources/questions.rst +++ b/doc/api/resources/questions.rst @@ -65,6 +65,7 @@ valid_date_max date Maximum value f valid_datetime_min datetime Minimum value for date and time questions (optional) valid_datetime_max datetime Maximum value for date and time questions (optional) valid_file_portrait boolean Turn on file validation for portrait photos +valid_string_length_min integer Minimum length for string questions (optional) valid_string_length_max integer Maximum length for string questions (optional) dependency_question integer Internal ID of a different question. The current question will only be shown if the question given in @@ -130,6 +131,7 @@ Endpoints "valid_date_max": null, "valid_datetime_min": null, "valid_datetime_max": null, + "valid_string_length_min": null, "valid_string_length_max": null, "valid_file_portrait": false, "dependency_question": null, @@ -211,6 +213,7 @@ Endpoints "valid_datetime_min": null, "valid_datetime_max": null, "valid_file_portrait": false, + "valid_string_length_min": null, "valid_string_length_max": null, "dependency_question": null, "dependency_value": null, @@ -315,6 +318,7 @@ Endpoints "valid_datetime_min": null, "valid_datetime_max": null, "valid_file_portrait": false, + "valid_string_length_min": null, "valid_string_length_max": null, "options": [ { @@ -399,6 +403,7 @@ Endpoints "valid_datetime_min": null, "valid_datetime_max": null, "valid_file_portrait": false, + "valid_string_length_min": null, "valid_string_length_max": null, "options": [ { diff --git a/src/pretix/api/serializers/item.py b/src/pretix/api/serializers/item.py index b34858c49..08a83f8cf 100644 --- a/src/pretix/api/serializers/item.py +++ b/src/pretix/api/serializers/item.py @@ -550,7 +550,7 @@ class QuestionSerializer(I18nAwareModelSerializer): 'ask_during_checkin', 'show_during_checkin', 'identifier', 'dependency_question', 'dependency_values', 'hidden', 'dependency_value', 'print_on_invoice', 'help_text', 'valid_number_min', 'valid_number_max', 'valid_date_min', 'valid_date_max', 'valid_datetime_min', 'valid_datetime_max', - 'valid_string_length_max', 'valid_file_portrait') + 'valid_string_length_max', 'valid_string_length_min', 'valid_file_portrait') def validate_identifier(self, value): Question._clean_identifier(self.context['event'], value, self.instance) diff --git a/src/pretix/base/forms/questions.py b/src/pretix/base/forms/questions.py index 98c3727e2..73cdb2998 100644 --- a/src/pretix/base/forms/questions.py +++ b/src/pretix/base/forms/questions.py @@ -708,6 +708,7 @@ class BaseQuestionsForm(forms.Form): elif q.type == Question.TYPE_STRING: field = forms.CharField( label=escape(q.question), required=required, + min_length=q.valid_string_length_min, max_length=q.valid_string_length_max, help_text=rich_text(q.help_text), initial=initial.answer if initial else None, @@ -715,6 +716,7 @@ class BaseQuestionsForm(forms.Form): elif q.type == Question.TYPE_TEXT: field = forms.CharField( label=escape(q.question), required=required, + min_length=q.valid_string_length_min, max_length=q.valid_string_length_max, help_text=rich_text(q.help_text), widget=forms.Textarea, diff --git a/src/pretix/base/migrations/0310_question_valid_string_length_min.py b/src/pretix/base/migrations/0310_question_valid_string_length_min.py new file mode 100644 index 000000000..618f974f7 --- /dev/null +++ b/src/pretix/base/migrations/0310_question_valid_string_length_min.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.12 on 2026-08-19 11:31 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("pretixbase", "0309_alter_questionanswer_unique_together_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="question", + name="valid_string_length_min", + field=models.PositiveIntegerField(null=True), + ), + ] diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py index 088c84789..6d4a5291a 100644 --- a/src/pretix/base/models/items.py +++ b/src/pretix/base/models/items.py @@ -50,7 +50,7 @@ from dateutil.tz import datetime_exists from django.conf import settings from django.core.exceptions import ValidationError from django.core.validators import ( - MaxLengthValidator, MinValueValidator, RegexValidator, + MaxLengthValidator, MinLengthValidator, MinValueValidator, RegexValidator, ) from django.db import models from django.db.models import Q @@ -1731,6 +1731,11 @@ class Question(LoggedModel): valid_datetime_max = models.DateTimeField(null=True, blank=True, verbose_name=_('Maximum value'), help_text=_('Currently not supported in our apps and during check-in')) + valid_string_length_min = models.PositiveIntegerField(null=True, blank=True, + verbose_name=_('Minimum length'), + help_text=_( + 'Currently not supported in our apps and during check-in' + )) valid_string_length_max = models.PositiveIntegerField(null=True, blank=True, verbose_name=_('Maximum length'), help_text=_( @@ -1885,6 +1890,11 @@ class Question(LoggedModel): else: raise ValidationError(_('Unknown country code.')) elif self.type in (Question.TYPE_STRING, Question.TYPE_TEXT): + if self.valid_string_length_min is not None and len(answer) < self.valid_string_length_min: + raise ValidationError(MinLengthValidator.message % { + 'limit_value': self.valid_string_length_min, + 'show_value': len(answer) + }) if self.valid_string_length_max is not None and len(answer) > self.valid_string_length_max: raise ValidationError(MaxLengthValidator.message % { 'limit_value': self.valid_string_length_max, @@ -1906,6 +1916,8 @@ class Question(LoggedModel): raise ValidationError(_("The maximum date must not be before the minimum value.")) if self.valid_number_max and self.valid_number_min and self.valid_number_min > self.valid_number_max: raise ValidationError(_("The maximum value must not be lower than the minimum value.")) + if self.valid_string_length_max and self.valid_string_length_min and self.valid_string_length_min > self.valid_string_length_max: + raise ValidationError(_("The maximum length must not be shorter than the minimum length.")) super().clean() def clean_type_change(self, old_type, new_type): diff --git a/src/pretix/control/forms/item.py b/src/pretix/control/forms/item.py index 1f2fceca1..b90592ffb 100644 --- a/src/pretix/control/forms/item.py +++ b/src/pretix/control/forms/item.py @@ -251,6 +251,7 @@ class QuestionForm(I18nModelForm): 'valid_date_min', 'valid_date_max', 'valid_file_portrait', + 'valid_string_length_min', 'valid_string_length_max', ] widgets = { diff --git a/src/pretix/control/templates/pretixcontrol/items/question_edit.html b/src/pretix/control/templates/pretixcontrol/items/question_edit.html index d241be565..864b42168 100644 --- a/src/pretix/control/templates/pretixcontrol/items/question_edit.html +++ b/src/pretix/control/templates/pretixcontrol/items/question_edit.html @@ -47,6 +47,7 @@ {% bootstrap_field form.valid_datetime_max layout="control" %}
+ {% bootstrap_field form.valid_string_length_min layout="control" %} {% bootstrap_field form.valid_string_length_max layout="control" %}
diff --git a/src/tests/api/test_items.py b/src/tests/api/test_items.py index 7b61601f0..aef2ef31f 100644 --- a/src/tests/api/test_items.py +++ b/src/tests/api/test_items.py @@ -2479,6 +2479,7 @@ TEST_QUESTION_RES = { "valid_datetime_min": None, "valid_datetime_max": None, "valid_file_portrait": False, + "valid_string_length_min": None, "valid_string_length_max": None, "help_text": {"en": "This is an example question"}, "options": [ diff --git a/src/tests/base/test_models.py b/src/tests/base/test_models.py index 41d9d67ab..349c8c37b 100644 --- a/src/tests/base/test_models.py +++ b/src/tests/base/test_models.py @@ -2970,8 +2970,9 @@ class SeatingTestCase(TestCase): @pytest.mark.django_db @pytest.mark.parametrize("qtype,answer,expected", [ - (Question.TYPE_STRING, "a", "a"), - (Question.TYPE_TEXT, "v", "v"), + (Question.TYPE_STRING, "aaa", "aaa"), + (Question.TYPE_STRING, "a", ValidationError), + (Question.TYPE_TEXT, "vvv", "vvv"), (Question.TYPE_TEXT, "waaaaay tooooo long", ValidationError), (Question.TYPE_NUMBER, "0.9", ValidationError), (Question.TYPE_NUMBER, "1", Decimal("1")), @@ -3025,6 +3026,7 @@ def test_question_answer_validation(qtype, answer, expected): valid_datetime_max=datetime.datetime(2018, 1, 16, 16, 0, 0, tzinfo=tzoffset(None, 3600)), valid_number_min=Decimal('1'), valid_number_max=Decimal('100'), + valid_string_length_min=3, valid_string_length_max=8, ) if isinstance(expected, type) and issubclass(expected, Exception): -- 2.54.0 From dbd971cc22432c4cdaddb85493cf40c5fb54316d Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Tue, 25 Aug 2026 09:38:11 +0200 Subject: [PATCH 089/249] API: fix writing old permissions on teams (#6489) --- src/pretix/api/serializers/organizer.py | 2 ++ src/tests/api/test_teams.py | 1 + 2 files changed, 3 insertions(+) diff --git a/src/pretix/api/serializers/organizer.py b/src/pretix/api/serializers/organizer.py index c62b91a3d..671c25d8e 100644 --- a/src/pretix/api/serializers/organizer.py +++ b/src/pretix/api/serializers/organizer.py @@ -426,6 +426,8 @@ class TeamSerializer(serializers.ModelSerializer): for k, v in OLD_TO_NEW_ORGANIZER_MIGRATION.items(): if full_data.get(k) is True: data["limit_organizer_permissions"].update({kk: True for kk in v}) + for key in list(k for k in data if k.startswith("can_")): + del data[key] if full_data.get('limit_events') and full_data.get('all_events'): raise ValidationError('Do not set both limit_events and all_events.') diff --git a/src/tests/api/test_teams.py b/src/tests/api/test_teams.py index dca30e609..c0cea19fa 100644 --- a/src/tests/api/test_teams.py +++ b/src/tests/api/test_teams.py @@ -83,6 +83,7 @@ def test_team_detail(token_client, organizer, event, second_team): TEST_TEAM_CREATE_PAYLOAD = { "name": "Foobar", "limit_events": ["dummy"], + "can_view_orders": True, } -- 2.54.0 From d7aae65777a0828cd61f4c4e591aff01ab34be5f Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Tue, 25 Aug 2026 09:39:21 +0200 Subject: [PATCH 090/249] API: fix tax rules create default handling (#6490) * API: fix tax rules create default handling * fix flake8 --- src/pretix/api/serializers/event.py | 8 ++++++-- src/tests/api/test_taxrules.py | 27 ++++++++++++++++----------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index 73e0b9c51..5ffa7cfbf 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -702,8 +702,12 @@ class TaxRuleSerializer(CountryFieldMixin, I18nAwareModelSerializer): return super().save(**kwargs) def validate_default(self, value): - if not value and self.instance.default: - raise ValidationError("You can't remove the default property, instead set it on another tax rule.") + if not value: + if self.instance: + if self.instance.default: + raise ValidationError("You can't remove the default property, instead set it on another tax rule.") + elif not self.context["event"].tax_rules.exists(): + raise ValidationError("You can't remove the default property as there is only one tax rule.") return value diff --git a/src/tests/api/test_taxrules.py b/src/tests/api/test_taxrules.py index 383272e47..7c47e6ece 100644 --- a/src/tests/api/test_taxrules.py +++ b/src/tests/api/test_taxrules.py @@ -61,17 +61,21 @@ def test_rule_detail(token_client, organizer, event, taxrule): @pytest.mark.django_db def test_rule_create(token_client, organizer, event): - resp = token_client.post( - '/api/v1/organizers/{}/events/{}/taxrules/'.format(organizer.slug, event.slug), - { - "name": {"en": "VAT", "de": "MwSt"}, - "rate": "19.00", - "price_includes_tax": True, - "eu_reverse_charge": False, - "home_country": "DE" - }, - format='json' - ) + url = '/api/v1/organizers/{}/events/{}/taxrules/'.format(organizer.slug, event.slug) + payload = { + "name": {"en": "VAT", "de": "MwSt"}, + "rate": "19.00", + "price_includes_tax": True, + "eu_reverse_charge": False, + "home_country": "DE", + "default": False, + } + # fail as only one rule exists and must be default + resp = token_client.post(url, payload, format='json') + assert resp.status_code == 400 + + del payload["default"] + resp = token_client.post(url, payload, format='json') assert resp.status_code == 201 rule = TaxRule.objects.get(pk=resp.data['id']) assert rule.name.data == {"en": "VAT", "de": "MwSt"} @@ -79,6 +83,7 @@ def test_rule_create(token_client, organizer, event): assert rule.price_includes_tax is True assert rule.eu_reverse_charge is False assert str(rule.home_country) == "DE" + assert rule.default is True @pytest.mark.django_db -- 2.54.0 From 4b4a301e6e25dbefe2fea0837d99edd838921e98 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 24 Aug 2026 17:49:57 +0200 Subject: [PATCH 091/249] Translations: Update German Currently translated at 100.0% (6419 of 6419 strings) Translation: pretix/pretix Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/ powered by weblate --- src/pretix/locale/de/LC_MESSAGES/django.po | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/pretix/locale/de/LC_MESSAGES/django.po b/src/pretix/locale/de/LC_MESSAGES/django.po index e205ea659..37ee35762 100644 --- a/src/pretix/locale/de/LC_MESSAGES/django.po +++ b/src/pretix/locale/de/LC_MESSAGES/django.po @@ -5,10 +5,10 @@ msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-24 15:35+0000\n" -"PO-Revision-Date: 2026-08-24 15:31+0000\n" +"PO-Revision-Date: 2026-08-25 00:00+0000\n" "Last-Translator: Raphael Michel \n" -"Language-Team: German \n" +"Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -31805,13 +31805,6 @@ msgid "Allow further payments during compliance hold" msgstr "Erlaube weitere Zahlungsversuche während PayPal eine Zahlung überprüft" #: pretix/plugins/paypal2/payment.py -#, fuzzy -#| msgid "" -#| "PayPals fraud prevention might block processing of individual payments " -#| "for a considerable amount of time. The payment is marked as \"pending\" " -#| "during this time window. You can allow your customers to start another " -#| "payment attempts during that window. This might result in them being " -#| "charged twice if theoriginal payment is approved." msgid "" "PayPals fraud prevention might block processing of individual payments for a " "considerable amount of time. The payment is marked as \"pending\" during " -- 2.54.0 From 0a6b8c0493216fdcfa100c1e2862acd8e7b45fe6 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 24 Aug 2026 17:49:58 +0200 Subject: [PATCH 092/249] Translations: Update German (informal) (de_Informal) Currently translated at 100.0% (6419 of 6419 strings) Translation: pretix/pretix Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/ powered by weblate --- src/pretix/locale/de_Informal/LC_MESSAGES/django.po | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po index 7435bef1c..0833b23b5 100644 --- a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po +++ b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-24 15:35+0000\n" -"PO-Revision-Date: 2026-08-24 15:31+0000\n" +"PO-Revision-Date: 2026-08-25 00:00+0000\n" "Last-Translator: Raphael Michel \n" "Language-Team: German (informal) \n" @@ -31761,13 +31761,6 @@ msgid "Allow further payments during compliance hold" msgstr "Erlaube weitere Zahlungsversuche während PayPal eine Zahlung überprüft" #: pretix/plugins/paypal2/payment.py -#, fuzzy -#| msgid "" -#| "PayPals fraud prevention might block processing of individual payments " -#| "for a considerable amount of time. The payment is marked as \"pending\" " -#| "during this time window. You can allow your customers to start another " -#| "payment attempts during that window. This might result in them being " -#| "charged twice if theoriginal payment is approved." msgid "" "PayPals fraud prevention might block processing of individual payments for a " "considerable amount of time. The payment is marked as \"pending\" during " -- 2.54.0 From 1414c22eb67d844b7809c21fd5c3c8c090664a31 Mon Sep 17 00:00:00 2001 From: CVZ-es Date: Mon, 24 Aug 2026 22:41:32 +0200 Subject: [PATCH 093/249] Translations: Update French Currently translated at 100.0% (6419 of 6419 strings) Translation: pretix/pretix Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/fr/ powered by weblate --- src/pretix/locale/fr/LC_MESSAGES/django.po | 187 +++++++++------------ 1 file changed, 77 insertions(+), 110 deletions(-) diff --git a/src/pretix/locale/fr/LC_MESSAGES/django.po b/src/pretix/locale/fr/LC_MESSAGES/django.po index 0041235ea..2f54e00ef 100644 --- a/src/pretix/locale/fr/LC_MESSAGES/django.po +++ b/src/pretix/locale/fr/LC_MESSAGES/django.po @@ -4,10 +4,10 @@ msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-08-24 15:35+0000\n" -"PO-Revision-Date: 2026-08-11 17:00+0000\n" -"Last-Translator: Hijiri Umemoto \n" -"Language-Team: French \n" +"PO-Revision-Date: 2026-08-25 00:00+0000\n" +"Last-Translator: CVZ-es \n" +"Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -89,7 +89,7 @@ msgstr "Hébreu" #: pretix/_base_settings.py msgid "Hungarian" -msgstr "" +msgstr "Hongrois" #: pretix/_base_settings.py msgid "Indonesian" @@ -438,10 +438,8 @@ msgid "You cannot exchange a medium for a medium." msgstr "Il n'est pas possible d'échanger un support contre un autre support." #: pretix/api/views/checkin.py -#, fuzzy -#| msgid "Product does not support medium exchange." msgid "You cannot simulate a medium exchange." -msgstr "Ce produit ne permet pas de changer de support." +msgstr "l n'est pas possible de simuler un échange de support." #: pretix/api/views/oauth.py pretix/control/logdisplay.py #, python-brace-format @@ -5833,7 +5831,7 @@ msgstr "Code pays (ISO 3166-1 alpha-2)" #: pretix/base/models/items.py msgid "Asked on" -msgstr "" +msgstr "Question posée le" #: pretix/base/models/items.py pretix/base/models/organizer.py msgid "" @@ -7555,9 +7553,6 @@ msgid "The payment for this invoice has already been received." msgstr "Le paiement de cette facture a déjà été reçu." #: pretix/base/payment.py -#, fuzzy -#| msgid "" -#| "This payment is already being processed and can not be canceled any more." msgid "" "This payment is already being processed and cannot be canceled any more." msgstr "" @@ -8340,16 +8335,12 @@ msgid "Presale end" msgstr "Fin de la prévente" #: pretix/base/reldate.py -#, fuzzy -#| msgid "Order email" msgid "Order creation" -msgstr "E-mail de la commande" +msgstr "Création d'une commande" #: pretix/base/reldate.py -#, fuzzy -#| msgid "Order expired" msgid "Order expiry" -msgstr "Commande expirée" +msgstr "Expiration de la commande" #: pretix/base/reldate.py msgid "before" @@ -8378,22 +8369,22 @@ msgstr "Non réglé" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as \"before\" for \"{}\"" -msgstr "" +msgstr "Une date relative ne peut pas être exprimée par « avant » pour « {} »" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as \"after\" for \"{}\"" -msgstr "" +msgstr "Une date relative ne peut pas être exprimée par « après » pour « {} »" #: pretix/base/reldate.py #, python-brace-format msgid "A relative time cannot be expressed as \"before\" for \"{}\"" -msgstr "" +msgstr "Une durée relative ne peut pas être exprimée par « avant » pour « {} »" #: pretix/base/reldate.py #, python-brace-format msgid "A relative time cannot be expressed as \"after\" for \"{}\"" -msgstr "" +msgstr "Un temps relatif ne peut pas être exprimé par « après » pour « {} »" #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" @@ -10430,12 +10421,12 @@ msgstr "" #: pretix/base/settings.py msgid "No dates match your criteria." -msgstr "" +msgstr "Aucune date ne correspond à vos critères." #: pretix/base/settings.py msgctxt "subevents" msgid "Text for empty date results" -msgstr "" +msgstr "Texte à afficher lorsque les résultats ne contiennent aucune date" #: pretix/base/settings.py msgctxt "subevents" @@ -10446,6 +10437,11 @@ msgid "" "touch with you to arrange further dates. We do not recommend more than one " "or two sentences." msgstr "" +"Ce texte s'affichera si le calendrier ou la liste des dates est vide, par " +"exemple parce qu'un mois ne comporte aucune date ou qu'un filtre sélectionné " +"par l'utilisateur ne donne aucun résultat. Vous pouvez en profiter pour " +"indiquer comment vous contacter afin de convenir d'autres dates. Nous vous " +"recommandons de ne pas dépasser une ou deux phrases." #: pretix/base/settings.py msgid "Guidance text" @@ -14479,28 +14475,20 @@ msgstr "" "plus l’année d’émission de la carte-cadeau." #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment date" msgid "Payment term" -msgstr "Date de paiement" +msgstr "Conditions de paiement" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "(Same as above)" msgid "same as above" -msgstr "(identique à ce qui précède)" +msgstr "idem que ci-dessus" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment term in days" msgid "different payment term in days" -msgstr "Délai de paiement en jours" +msgstr "délai de paiement différent en jours" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment term in minutes" msgid "different payment term in minutes" -msgstr "Délai de paiement en minutes" +msgstr "durée de paiement différente en minutes" #: pretix/control/forms/event.py msgid "Prices including tax" @@ -20577,26 +20565,20 @@ msgstr "" "CNAME comme ceci :" #: pretix/control/templates/pretixcontrol/email_setup_simple.html -#, fuzzy -#| msgid "" -#| "We found an SPF record on your domain that includes this system. Great!" msgid "We found a DKIM record on your domain for this system. Great!" msgstr "" -"Nous avons trouvé un enregistrement SPF sur votre domaine qui inclut ce " -"système. Super !" +"Nous avons trouvé un enregistrement DKIM sur votre domaine pour ce système. " +"Parfait !" #: pretix/control/templates/pretixcontrol/email_setup_simple.html msgid "Your new DMARC record could look like this:" msgstr "Votre nouvel enregistrement DMARC pourrait ressembler à ceci :" #: pretix/control/templates/pretixcontrol/email_setup_simple.html -#, fuzzy -#| msgid "" -#| "We found an SPF record on your domain that includes this system. Great!" msgid "We found a DMARC record on your domain for this system. Great!" msgstr "" -"Nous avons trouvé un enregistrement SPF sur votre domaine qui inclut ce " -"système. Super !" +"Nous avons trouvé un enregistrement DMARC sur votre domaine pour ce système. " +"Parfait !" #: pretix/control/templates/pretixcontrol/email_setup_simple.html msgid "Verification" @@ -23407,28 +23389,24 @@ msgstr "" "besoins alimentaires." #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Create a new question" msgid "Create a new per-ticket question" -msgstr "Créer une nouvelle question" +msgstr "Créer une nouvelle question spécifique à chaque ticket" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Create a new question" msgid "Create a new order-level question" -msgstr "Créer une nouvelle question" +msgstr "Créer une nouvelle question au niveau de la commande" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Delete question" msgid "Per-ticket questions" -msgstr "Supprimer la question" +msgstr "Questions relatives à chaque billet" #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "These questions are asked for every ticket, so possibly multiple times in " "the same order." msgstr "" +"Ces questions sont posées pour chaque billet, et peuvent donc être posées " +"plusieurs fois au cours d'une même commande." #: pretix/control/templates/pretixcontrol/items/questions.html msgid "Create a new question" @@ -23447,28 +23425,28 @@ msgid "All personalized products" msgstr "Tous les produits personnalisés" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Include questions" msgid "Per-order questions" -msgstr "Inclure des questions" +msgstr "Questions relatives à chaque commande" #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "This functionality is in active development and expected to change " "significantly over the coming months." msgstr "" +"Cette fonctionnalité est en cours de développement et devrait évoluer " +"considérablement au cours des prochains mois." #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "Per-order questions are currently not supported and will not be displayed in " "pretixPOS." msgstr "" +"Les questions spécifiques à chaque commande ne sont actuellement pas prises " +"en charge et n'apparaîtront pas dans pretixPOS." #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "The question has been reordered." msgid "These questions are asked once per order." -msgstr "La question a été réordonnée." +msgstr "Ces questions sont posées une fois par commande." #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html @@ -24080,10 +24058,8 @@ msgstr "(optionnel)" #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/checkout_questions.html #: pretix/presale/templates/pretixpresale/event/order_modify.html -#, fuzzy -#| msgid "Additional information" msgid "Additional order information" -msgstr "Informations complémentaires" +msgstr "Informations complémentaires sur la commande" #: pretix/control/templates/pretixcontrol/order/delete.html msgid "Delete order" @@ -25799,14 +25775,13 @@ msgid "Hardware model" msgstr "Modèle de matériel" #: pretix/control/templates/pretixcontrol/organizers/devices.html -#, fuzzy, python-format -#| msgid "Begin: %(time)s" +#, python-format msgid "Last seen: %(time)s" -msgstr "Début : %(time)s" +msgstr "Dernière connexion : %(time)s" #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "No recent contact" -msgstr "" +msgstr "Aucun contact récent" #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "Not yet initialized" @@ -29286,13 +29261,6 @@ msgstr "" "l'enregistrement SPF." #: pretix/control/views/mailsetup.py -#, fuzzy -#| msgid "" -#| "We could not find an SPF record set for the domain you are trying to use. " -#| "This means that there is a very high change most of the emails will be " -#| "rejected or marked as spam. We strongly recommend setting an SPF record " -#| "on the domain. You can do so through the DNS settings at the provider you " -#| "registered your domain with." msgid "" "We could not find a CNAME record pointing to our DKIM key for domain you are " "trying to use. This means that there is a very high change most of the " @@ -29300,53 +29268,35 @@ msgid "" "DKIM through a CNAME record. You can do so through the DNS settings at the " "provider you registered your domain with." msgstr "" -"Nous n'avons pas trouvé d'enregistrement SPF pour le domaine que vous " -"essayez d'utiliser. Cela signifie qu'il y a de fortes chances que la plupart " -"des e-mails soient rejetés ou marqués comme spam. Nous vous recommandons " -"vivement de configurer un enregistrement SPF sur le domaine. Vous pouvez le " -"faire via les paramètres DNS chez le fournisseur auprès duquel vous avez " -"enregistré votre domaine." +"Nous n'avons pas trouvé d'enregistrement CNAME pointant vers notre clé DKIM " +"pour le domaine que vous essayez d'utiliser. Cela signifie qu'il y a de très " +"fortes chances que la plupart des e-mails soient rejetés ou marqués comme " +"spam. Nous vous recommandons vivement de configurer DKIM à l'aide d'un " +"enregistrement CNAME. Vous pouvez le faire via les paramètres DNS chez le " +"fournisseur auprès duquel vous avez enregistré votre domaine." #: pretix/control/views/mailsetup.py -#, fuzzy -#| msgid "" -#| "We found an SPF record set for the domain you are trying to use, but it " -#| "does not include this system's email server. This means that there is a " -#| "very high chance most of the emails will be rejected or marked as spam. " -#| "You should update the DNS settings of your domain to include this system " -#| "in the SPF record." msgid "" "We found a CNAME record for a DKIM key, but it is not pointing to the right " "location. This means that there is a very high chance most of the emails " "will be rejected or marked as spam. You should update the DNS settings of " "your domain." msgstr "" -"Nous avons trouvé un enregistrement SPF défini pour le domaine que vous " -"essayez d'utiliser, mais il n'inclut pas le serveur de messagerie de ce " -"système. Cela signifie qu'il y a de fortes chances que la plupart des e-" -"mails soient rejetés ou marqués comme spam. Vous devez mettre à jour les " -"paramètres DNS de votre domaine afin d'inclure ce système dans " -"l'enregistrement SPF." +"Nous avons détecté un enregistrement CNAME associé à une clé DKIM, mais " +"celui-ci ne pointe pas vers la bonne adresse. Cela signifie qu'il y a de " +"très fortes chances que la plupart de vos e-mails soient rejetés ou classés " +"comme spam. Vous devez mettre à jour les paramètres DNS de votre domaine." #: pretix/control/views/mailsetup.py -#, fuzzy -#| msgid "" -#| "We found an SPF record set for the domain you are trying to use, but it " -#| "does not include this system's email server. This means that there is a " -#| "very high chance most of the emails will be rejected or marked as spam. " -#| "You should update the DNS settings of your domain to include this system " -#| "in the SPF record." msgid "" "We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" -"Nous avons trouvé un enregistrement SPF défini pour le domaine que vous " -"essayez d'utiliser, mais il n'inclut pas le serveur de messagerie de ce " -"système. Cela signifie qu'il y a de fortes chances que la plupart des e-" -"mails soient rejetés ou marqués comme spam. Vous devez mettre à jour les " -"paramètres DNS de votre domaine afin d'inclure ce système dans " -"l'enregistrement SPF." +"Nous n'avons pas trouvé d'enregistrement DMARC pour votre domaine. Cela " +"signifie qu'il y a de très fortes chances que la plupart de vos e-mails " +"soient rejetés ou marqués comme spam. Vous devriez mettre à jour les " +"paramètres DNS de votre domaine." #: pretix/control/views/mailsetup.py msgid "The verification code was incorrect, please try again." @@ -32032,8 +31982,8 @@ msgid "" "We're waiting for an answer from PayPal regarding your payment. Please " "contact us, if this takes more than a few hours." msgstr "" -"Nous attendons une réponse de PayPal concernant votre paiement. Veuillez " -"nous contacter, si cela prend plus de quelques heures." +"Nous attendons une réponse de PayPal concernant votre paiement. N'hésitez " +"pas à nous contacter si cela prend plus de quelques heures." #: pretix/plugins/paypal/views.py pretix/plugins/paypal2/views.py msgid "Invalid response from PayPal received." @@ -32131,6 +32081,8 @@ msgstr "" #: pretix/plugins/paypal2/payment.py msgid "Allow further payments during compliance hold" msgstr "" +"Autoriser la poursuite des paiements pendant la période de suspension pour " +"non-conformité" #: pretix/plugins/paypal2/payment.py msgid "" @@ -32140,16 +32092,25 @@ msgid "" "attempts during that window. This might result in them being charged twice " "if the original payment is approved." msgstr "" +"Le système de prévention des fraudes de PayPal peut bloquer le traitement de " +"certains paiements pendant une durée considérable. Pendant cette période, le " +"paiement est marqué comme « en attente ». Vous pouvez autoriser vos clients " +"à effectuer de nouvelles tentatives de paiement pendant cette période. Cela " +"peut entraîner un double prélèvement si le paiement initial est finalement " +"approuvé." #: pretix/plugins/paypal2/payment.py msgid "Timeout further payment attempts" -msgstr "" +msgstr "Délai d'expiration des tentatives de paiement supplémentaires" #: pretix/plugins/paypal2/payment.py msgid "" "Time duration in minutes after which another payment attempt is possible, " "while the last payment is still under investigation." msgstr "" +"Durée en minutes à l'issue de laquelle une nouvelle tentative de paiement " +"est possible, alors que le dernier paiement fait toujours l'objet d'une " +"enquête." #: pretix/plugins/paypal2/payment.py msgid "-- Automatic --" @@ -32427,6 +32388,12 @@ msgid "" "twice in case PayPal allows your initial payment attempt. Please contact us " "to resolve this case." msgstr "" +"Votre paiement est en cours de traitement par PayPal. Cette opération prend " +"plus de temps que d'habitude. Vous pouvez attendre que PayPal valide le " +"paiement ou essayer de payer à nouveau en utilisant ce moyen de paiement ou " +"un autre. Cela pourrait entraîner un double prélèvement si PayPal autorise " +"votre première tentative de paiement. Veuillez nous contacter pour résoudre " +"ce problème." #: pretix/plugins/paypal2/views.py msgid "" -- 2.54.0 From aa583a291fb4abf11be49b7cda189fcbb98073b1 Mon Sep 17 00:00:00 2001 From: CVZ-es Date: Mon, 24 Aug 2026 23:06:29 +0200 Subject: [PATCH 094/249] Translations: Update Spanish Currently translated at 100.0% (6419 of 6419 strings) Translation: pretix/pretix Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/ powered by weblate --- src/pretix/locale/es/LC_MESSAGES/django.po | 287 ++++++++------------- 1 file changed, 111 insertions(+), 176 deletions(-) diff --git a/src/pretix/locale/es/LC_MESSAGES/django.po b/src/pretix/locale/es/LC_MESSAGES/django.po index ec3014eb7..3d9066be7 100644 --- a/src/pretix/locale/es/LC_MESSAGES/django.po +++ b/src/pretix/locale/es/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ 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-07-08 16:00+0000\n" +"PO-Revision-Date: 2026-08-25 00:00+0000\n" "Last-Translator: CVZ-es \n" "Language-Team: Spanish \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.8.1\n" #: pretix/_base_settings.py msgid "English" @@ -93,7 +93,7 @@ msgstr "Hebreo" #: pretix/_base_settings.py msgid "Hungarian" -msgstr "" +msgstr "Húngaro" #: pretix/_base_settings.py msgid "Indonesian" @@ -439,10 +439,8 @@ msgid "You cannot exchange a medium for a medium." msgstr "No se puede cambiar un medio por otro." #: pretix/api/views/checkin.py -#, fuzzy -#| msgid "Product does not support medium exchange." msgid "You cannot simulate a medium exchange." -msgstr "Este producto no admite el cambio de medio." +msgstr "No se puede simular un intercambio de medio." #: pretix/api/views/oauth.py pretix/control/logdisplay.py #, python-brace-format @@ -1401,10 +1399,8 @@ msgid "Membership type" msgstr "Tipo de suscripción" #: pretix/base/exporters/customers.py -#, fuzzy -#| msgid "Purchase time" msgid "Purchase ticket" -msgstr "Hora de compra" +msgstr "Comprar entrada" #: pretix/base/exporters/customers.py pretix/base/exporters/orderlist.py #: pretix/base/exporters/waitinglist.py pretix/base/forms/questions.py @@ -1421,8 +1417,6 @@ msgid "Start date" msgstr "Fecha de inicio" #: pretix/base/exporters/customers.py -#, fuzzy -#| msgid "Start time from" msgid "Start time" msgstr "Hora de inicio" @@ -1437,10 +1431,8 @@ msgid "End date" msgstr "Fecha final" #: pretix/base/exporters/customers.py -#, fuzzy -#| msgid "End: %(time)s" msgid "End time" -msgstr "Fin: %(time)s" +msgstr "Hora de finalización" #: pretix/base/exporters/dekodi.py pretix/base/exporters/invoices.py msgctxt "export_category" @@ -4687,16 +4679,13 @@ msgid "This event is remote or partially remote." msgstr "Este evento es remoto o parcialmente remoto." #: pretix/base/models/event.py -#, fuzzy -#| msgid "" -#| "This will be used to let users know if the event is in a different " -#| "timezone and let’s us calculate users’ local times." msgid "" "This will be used to let users know if the event is in a different timezone, " "and to let us calculate the local time of a user." msgstr "" -"Esto se utilizará para que los usuarios sepan si el evento se celebra en una " -"zona horaria diferente y nos permite calcular la hora local de los usuarios." +"Esto servirá para informar a los usuarios de si el evento se celebra en una " +"zona horaria diferente y para que podamos calcular la hora local de cada " +"usuario." #: pretix/base/models/event.py pretix/base/models/organizer.py #: pretix/control/navigation.py @@ -5823,7 +5812,7 @@ msgstr "Código de país (ISO 3166-1 alfa-2)" #: pretix/base/models/items.py msgid "Asked on" -msgstr "" +msgstr "Preguntado el" #: pretix/base/models/items.py pretix/base/models/organizer.py msgid "" @@ -7523,9 +7512,6 @@ msgid "The payment for this invoice has already been received." msgstr "El pago de esta factura ya se ha recibido." #: pretix/base/payment.py -#, fuzzy -#| msgid "" -#| "This payment is already being processed and can not be canceled any more." msgid "" "This payment is already being processed and cannot be canceled any more." msgstr "Este pago ya se está procesando y ya no se puede cancelar." @@ -7652,15 +7638,12 @@ msgid "This gift card was used in the meantime. Please try again." msgstr "Mientras tanto, esta tarjeta de regalo se utilizó. Inténtalo de nuevo." #: pretix/base/payment.py -#, fuzzy -#| msgid "" -#| "This payment provider does not exist or the respective plugin is disabled." msgid "" "This payment provider exists for historical purposes only and is no longer " "usable." msgstr "" -"Este proveedor de pago no existe o el plugin correspondiente está " -"desactivado." +"Este proveedor de pagos se mantiene únicamente con fines históricos y ya no " +"se puede utilizar." #: pretix/base/pdf.py msgid "Ticket code (barcode content)" @@ -8302,16 +8285,12 @@ msgid "Presale end" msgstr "Fin de la preventa" #: pretix/base/reldate.py -#, fuzzy -#| msgid "Order email" msgid "Order creation" -msgstr "Correo electrónico del pedido" +msgstr "Creación de pedidos" #: pretix/base/reldate.py -#, fuzzy -#| msgid "Order expired" msgid "Order expiry" -msgstr "Pedido caducado" +msgstr "Caducidad del pedido" #: pretix/base/reldate.py msgid "before" @@ -8340,22 +8319,22 @@ msgstr "No fijado" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as \"before\" for \"{}\"" -msgstr "" +msgstr "Una fecha relativa no puede expresarse como «antes de» para «{}»" #: pretix/base/reldate.py #, python-brace-format msgid "A relative date cannot be expressed as \"after\" for \"{}\"" -msgstr "" +msgstr "Una fecha relativa no puede expresarse como «después de» para «{}»" #: pretix/base/reldate.py #, python-brace-format msgid "A relative time cannot be expressed as \"before\" for \"{}\"" -msgstr "" +msgstr "Un tiempo relativo no puede expresarse como «antes de» para «{}»" #: pretix/base/reldate.py #, python-brace-format msgid "A relative time cannot be expressed as \"after\" for \"{}\"" -msgstr "" +msgstr "Un tiempo relativo no puede expresarse como «después de» para «{}»" #: pretix/base/secrets.py msgid "Random (default, works with all pretix apps)" @@ -9597,16 +9576,12 @@ msgstr "" "una tarjeta regalo." #: pretix/base/services/orders.py -#, fuzzy -#| msgid "" -#| "You cannot change the price of a position that has been used to issue a " -#| "gift card." msgid "" "You cannot change the ticket secret of a position that has been used to " "issue a gift card." msgstr "" -"No se puede cambiar el precio de una posición que se ha usado para entregar " -"una tarjeta regalo." +"No se puede modificar el código secreto de un ticket correspondiente a una " +"posición que se haya utilizado para emitir una tarjeta regalo." #: pretix/base/services/orders.py #, python-brace-format @@ -10385,12 +10360,12 @@ msgstr "" #: pretix/base/settings.py msgid "No dates match your criteria." -msgstr "" +msgstr "No hay fechas que se ajusten a tus criterios." #: pretix/base/settings.py msgctxt "subevents" msgid "Text for empty date results" -msgstr "" +msgstr "Texto para los resultados con fechas vacías" #: pretix/base/settings.py msgctxt "subevents" @@ -10401,6 +10376,11 @@ msgid "" "touch with you to arrange further dates. We do not recommend more than one " "or two sentences." msgstr "" +"Este texto aparecerá si el calendario o la lista de fechas están vacíos, por " +"ejemplo, porque un mes no contiene ninguna fecha o porque el filtro " +"seleccionado por el usuario no arroja ningún resultado. Puedes aprovecharlo " +"para indicar cómo ponerse en contacto contigo para concertar otras citas. No " +"recomendamos que superen una o dos frases." #: pretix/base/settings.py msgid "Guidance text" @@ -14360,28 +14340,20 @@ msgstr "" "año en el que se emite la tarjeta de regalo." #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment date" msgid "Payment term" -msgstr "Fecha de pago" +msgstr "Condiciones de pago" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "(Same as above)" msgid "same as above" -msgstr "(Lo mismo que arriba)" +msgstr "igual que arriba" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment term in days" msgid "different payment term in days" -msgstr "Plazo de pago en días" +msgstr "plazo de pago diferente en días" #: pretix/control/forms/event.py -#, fuzzy -#| msgid "Payment term in minutes" msgid "different payment term in minutes" -msgstr "Plazo de pago en minutos" +msgstr "plazo de pago diferente en minutos" #: pretix/control/forms/event.py msgid "Prices including tax" @@ -15136,7 +15108,7 @@ msgstr "Fecha final" #: pretix/control/forms/filter.py msgid "Start time from" -msgstr "Hora de inicio" +msgstr "Hora de inicio a partir de" #: pretix/control/forms/filter.py msgid "Start time until" @@ -15396,10 +15368,8 @@ msgid "Source" msgstr "Fuente" #: pretix/control/forms/filter.py -#, fuzzy -#| msgid "All vouchers" msgid "All sources" -msgstr "Todos los vales de compra" +msgstr "Todas las fuentes" #: pretix/control/forms/filter.py msgid "Team actions" @@ -15410,16 +15380,12 @@ msgid "Customer actions" msgstr "Acciones de los clientes" #: pretix/control/forms/filter.py -#, fuzzy -#| msgid "Device status" msgid "Device actions" -msgstr "Estado de los dispositivos" +msgstr "Acciones del dispositivo" #: pretix/control/forms/filter.py -#, fuzzy -#| msgid "Order email" msgid "User email" -msgstr "Correo electrónico del pedido" +msgstr "Correo electrónico del usuario" #: pretix/control/forms/filter.py pretix/control/navigation.py msgid "All users" @@ -16941,40 +16907,33 @@ msgid "" "because at least one of the selected vouchers has already been redeemed " "%(max_redeemed)s times." msgstr "" +"No puedes reducir el número máximo de canjes a %(max_usages)s, ya que al " +"menos uno de los vales seleccionados ya se ha canjeado %(max_redeemed)s " +"veces." #: pretix/control/forms/vouchers.py -#, fuzzy -#| msgid "" -#| "You cannot create a voucher that blocks quota as the selected product or " -#| "quota is currently sold out or completely reserved." msgid "" "You cannot create a voucher that allows selection of a quota but has no date " "selected." msgstr "" -"No se puede crear un vale de compra que bloquee la cuota ya que el producto " -"seleccionado o la cuota está agotada o completamente reservada." +"No se puede crear un comprobante que permita seleccionar una cuota pero en " +"el que no se haya seleccionado ninguna fecha." #: pretix/control/forms/vouchers.py -#, fuzzy -#| msgid "The selected product does not allow to select a seat." msgid "The selected quota does not match the selected subevent." -msgstr "El producto seleccionado no permite seleccionar una butaca." +msgstr "La cuota seleccionada no coincide con el subevento seleccionado." #: pretix/control/forms/vouchers.py -#, fuzzy -#| msgid "" -#| "There is not enough quota available on quota \"{}\" to perform the " -#| "operation." msgid "There is no sufficient quota available to perform this change." -msgstr "" -"No hay suficiente cuota disponible en la cuota \"{}\" para realizar esta " -"operación." +msgstr "No hay cuota suficiente disponible para realizar este cambio." #: pretix/control/forms/vouchers.py msgid "" "Changing the maximum number of usages in bulk is not supported if any of the " "selected vouchers is assigned a seat." msgstr "" +"No es posible modificar de forma masiva el número máximo de usos si a alguno " +"de los vales seleccionados se le ha asignado una plaza." #: pretix/control/forms/vouchers.py msgctxt "subevent" @@ -16982,18 +16941,24 @@ msgid "" "Changing the date in bulk is not supported if any of the selected vouchers " "is assigned a seat." msgstr "" +"No es posible modificar la fecha de forma masiva si a alguno de los " +"comprobantes seleccionados se le ha asignado una plaza." #: pretix/control/forms/vouchers.py msgid "" "Changing the product to a quota is not supported if any of the selected " "vouchers is assigned a seat." msgstr "" +"No es posible cambiar el producto a una cuota si a alguno de los vales " +"seleccionados se le ha asignado una plaza." #: pretix/control/forms/vouchers.py msgid "" "This change cannot be completed because not all assigned seats of the " "vouchers are still available" msgstr "" +"No es posible completar este cambio porque no todos los asientos asignados " +"de los vales siguen estando disponibles" #: pretix/control/forms/vouchers.py msgid "Codes" @@ -20448,34 +20413,24 @@ msgstr "" "¡Genial!" #: pretix/control/templates/pretixcontrol/email_setup_simple.html -#, fuzzy -#| msgid "Your new SPF record could look like this:" msgid "Your new DKIM record should be set up as a CNAME record like this:" -msgstr "El nuevo registro SPF podría tener el siguiente aspecto:" +msgstr "" +"El nuevo registro DKIM debe configurarse como un registro CNAME de la " +"siguiente manera:" #: pretix/control/templates/pretixcontrol/email_setup_simple.html -#, fuzzy -#| msgid "" -#| "We found an SPF record on your domain that includes this system. Great!" msgid "We found a DKIM record on your domain for this system. Great!" msgstr "" -"Hemos encontrado un registro SPF en su dominio que incluye este sistema. " -"¡Genial!" +"Hemos encontrado un registro DKIM en tu dominio para este sistema. ¡Genial!" #: pretix/control/templates/pretixcontrol/email_setup_simple.html -#, fuzzy -#| msgid "Your new SPF record could look like this:" msgid "Your new DMARC record could look like this:" -msgstr "El nuevo registro SPF podría tener el siguiente aspecto:" +msgstr "El nuevo registro DMARC podría tener este aspecto:" #: pretix/control/templates/pretixcontrol/email_setup_simple.html -#, fuzzy -#| msgid "" -#| "We found an SPF record on your domain that includes this system. Great!" msgid "We found a DMARC record on your domain for this system. Great!" msgstr "" -"Hemos encontrado un registro SPF en su dominio que incluye este sistema. " -"¡Genial!" +"Hemos encontrado un registro DMARC en tu dominio para este sistema. ¡Genial!" #: pretix/control/templates/pretixcontrol/email_setup_simple.html msgid "Verification" @@ -22186,10 +22141,8 @@ msgid "The quick brown fox jumps over the lazy dog." msgstr "El veloz zorro marrón salta sobre el perro perezoso." #: pretix/control/templates/pretixcontrol/fragment_log_filter_form.html -#, fuzzy -#| msgid "Specific seat" msgid "Specific object selected" -msgstr "Butaca especifica" +msgstr "Se ha seleccionado un objeto concreto" #: pretix/control/templates/pretixcontrol/fragment_quota_box.html #: pretix/control/templates/pretixcontrol/fragment_quota_box_paid.html @@ -23262,28 +23215,24 @@ msgstr "" "sus usuarios acerca de las necesidades dietéticas." #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Create a new question" msgid "Create a new per-ticket question" -msgstr "Crear una nueva pregunta" +msgstr "Crear una nueva pregunta por ticket" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Create a new question" msgid "Create a new order-level question" -msgstr "Crear una nueva pregunta" +msgstr "Crear una nueva pregunta a nivel de pedido" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Delete question" msgid "Per-ticket questions" -msgstr "Borrar pregunta" +msgstr "Preguntas por entrada" #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "These questions are asked for every ticket, so possibly multiple times in " "the same order." msgstr "" +"Estas preguntas se formulan para cada entrada, por lo que es posible que se " +"repitan varias veces en el mismo pedido." #: pretix/control/templates/pretixcontrol/items/questions.html msgid "Create a new question" @@ -23302,28 +23251,28 @@ msgid "All personalized products" msgstr "Todos los productos personalizados" #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "Include questions" msgid "Per-order questions" -msgstr "Incluir preguntas" +msgstr "Preguntas por pedido" #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "This functionality is in active development and expected to change " "significantly over the coming months." msgstr "" +"Esta funcionalidad se encuentra en fase de desarrollo activo y se prevé que " +"sufra cambios significativos en los próximos meses." #: pretix/control/templates/pretixcontrol/items/questions.html msgid "" "Per-order questions are currently not supported and will not be displayed in " "pretixPOS." msgstr "" +"Actualmente no se admiten las preguntas por pedido y no se mostrarán en " +"pretixPOS." #: pretix/control/templates/pretixcontrol/items/questions.html -#, fuzzy -#| msgid "The question has been reordered." msgid "These questions are asked once per order." -msgstr "La pregunta ha sido reordenada." +msgstr "Estas preguntas se formulan una vez por pedido." #: pretix/control/templates/pretixcontrol/items/quota.html #: pretix/control/templates/pretixcontrol/items/quota_edit.html @@ -23855,6 +23804,8 @@ msgid "" "Ticket secrets of order positions that have been used to issue a gift card " "can not be changed. Only the link will be changed in this case." msgstr "" +"Los datos de los pedidos que se han utilizado para emitir una tarjeta regalo " +"no se pueden modificar. En este caso, solo se modificará el enlace." #: pretix/control/templates/pretixcontrol/order/change.html msgid "" @@ -23930,10 +23881,8 @@ msgstr "(opcional)" #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html #: pretix/presale/templates/pretixpresale/event/checkout_questions.html #: pretix/presale/templates/pretixpresale/event/order_modify.html -#, fuzzy -#| msgid "Additional information" msgid "Additional order information" -msgstr "Información adicional" +msgstr "Información adicional sobre el pedido" #: pretix/control/templates/pretixcontrol/order/delete.html msgid "Delete order" @@ -25635,14 +25584,13 @@ msgid "Hardware model" msgstr "Modelo del Hardware" #: pretix/control/templates/pretixcontrol/organizers/devices.html -#, fuzzy, python-format -#| msgid "Begin: %(time)s" +#, python-format msgid "Last seen: %(time)s" -msgstr "Inicio: %(time)s" +msgstr "Última conexión: %(time)s" #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "No recent contact" -msgstr "" +msgstr "No ha habido contacto reciente" #: pretix/control/templates/pretixcontrol/organizers/devices.html msgid "Not yet initialized" @@ -27991,10 +27939,8 @@ msgstr "" "producto!" #: pretix/control/templates/pretixcontrol/vouchers/bulk_edit.html -#, fuzzy -#| msgid "Create multiple vouchers" msgid "Change multiple vouchers" -msgstr "Crear múltiples vales de compra" +msgstr "Modificar varios vales" #: pretix/control/templates/pretixcontrol/vouchers/delete.html #: pretix/control/templates/pretixcontrol/vouchers/detail.html @@ -28405,6 +28351,9 @@ msgid "" "team. If you want to add a different user or create a new account, log out " "and click the invitation link again." msgstr "" +"No puedes aceptar la invitación para «{}», ya que ya formas parte de este " +"equipo. Si quieres añadir a otro usuario o crear una nueva cuenta, cierra la " +"sesión y vuelve a hacer clic en el enlace de invitación." #: pretix/control/views/auth.py #, python-brace-format @@ -29088,13 +29037,6 @@ msgstr "" "SPF." #: pretix/control/views/mailsetup.py -#, fuzzy -#| msgid "" -#| "We could not find an SPF record set for the domain you are trying to use. " -#| "This means that there is a very high change most of the emails will be " -#| "rejected or marked as spam. We strongly recommend setting an SPF record " -#| "on the domain. You can do so through the DNS settings at the provider you " -#| "registered your domain with." msgid "" "We could not find a CNAME record pointing to our DKIM key for domain you are " "trying to use. This means that there is a very high change most of the " @@ -29102,53 +29044,35 @@ msgid "" "DKIM through a CNAME record. You can do so through the DNS settings at the " "provider you registered your domain with." msgstr "" -"No se pudo encontrar un registro SPF configurado para el dominio que está " -"intentando usar. Esto significa que existe una alta probabilidad de que la " -"mayoría de los correos electrónicos sean rechazados o marcados como spam. " -"Recomendamos encarecidamente configurar un registro SPF en el dominio. Puede " -"hacerlo a través de la configuración de DNS en el proveedor con el que " -"registró su dominio." +"No hemos podido encontrar un registro CNAME que apunte a nuestra clave DKIM " +"para el dominio que estás intentando utilizar. Esto significa que hay una " +"probabilidad muy alta de que la mayoría de los correos electrónicos sean " +"rechazados o marcados como spam. Te recomendamos encarecidamente que " +"configures DKIM mediante un registro CNAME. Puedes hacerlo a través de la " +"configuración de DNS del proveedor con el que registraste tu dominio." #: pretix/control/views/mailsetup.py -#, fuzzy -#| msgid "" -#| "We found an SPF record set for the domain you are trying to use, but it " -#| "does not include this system's email server. This means that there is a " -#| "very high chance most of the emails will be rejected or marked as spam. " -#| "You should update the DNS settings of your domain to include this system " -#| "in the SPF record." msgid "" "We found a CNAME record for a DKIM key, but it is not pointing to the right " "location. This means that there is a very high chance most of the emails " "will be rejected or marked as spam. You should update the DNS settings of " "your domain." msgstr "" -"Hemos encontrado un registro SPF configurado para el dominio que está " -"intentando utilizar, pero no incluye el servidor de correo electrónico del " -"mismo sistema. Esto significa que es muy probable que la mayoría de los " -"correos electrónicos sean rechazados o marcados como spam. Debe actualizar " -"la configuración DNS de su dominio para incluir este sistema en el registro " -"SPF." +"Hemos encontrado un registro CNAME para una clave DKIM, pero no apunta a la " +"ubicación correcta. Esto significa que hay muchas posibilidades de que la " +"mayoría de los correos electrónicos sean rechazados o marcados como spam. " +"Deberías actualizar la configuración DNS de tu dominio." #: pretix/control/views/mailsetup.py -#, fuzzy -#| msgid "" -#| "We found an SPF record set for the domain you are trying to use, but it " -#| "does not include this system's email server. This means that there is a " -#| "very high chance most of the emails will be rejected or marked as spam. " -#| "You should update the DNS settings of your domain to include this system " -#| "in the SPF record." msgid "" "We did not find a DMARC record for your domain. This means that there is a " "very high chance most of the emails will be rejected or marked as spam. You " "should update the DNS settings of your domain." msgstr "" -"Hemos encontrado un registro SPF configurado para el dominio que está " -"intentando utilizar, pero no incluye el servidor de correo electrónico del " -"mismo sistema. Esto significa que es muy probable que la mayoría de los " -"correos electrónicos sean rechazados o marcados como spam. Debe actualizar " -"la configuración DNS de su dominio para incluir este sistema en el registro " -"SPF." +"No hemos encontrado ningún registro DMARC para tu dominio. Esto significa " +"que hay muchas posibilidades de que la mayoría de los correos electrónicos " +"sean rechazados o marcados como spam. Deberías actualizar la configuración " +"DNS de tu dominio." #: pretix/control/views/mailsetup.py msgid "The verification code was incorrect, please try again." @@ -31901,6 +31825,7 @@ msgstr "" #: pretix/plugins/paypal2/payment.py msgid "Allow further payments during compliance hold" msgstr "" +"Permitir que se realicen más pagos durante la suspensión por incumplimiento" #: pretix/plugins/paypal2/payment.py msgid "" @@ -31910,16 +31835,24 @@ msgid "" "attempts during that window. This might result in them being charged twice " "if the original payment is approved." msgstr "" +"El sistema de prevención de fraudes de PayPal podría bloquear la tramitación " +"de pagos individuales durante un periodo de tiempo considerable. Durante ese " +"intervalo, el pago aparece como «pendiente». Puedes permitir que tus " +"clientes realicen nuevos intentos de pago durante ese intervalo. Esto podría " +"dar lugar a que se les cobre dos veces si se aprueba el pago original." #: pretix/plugins/paypal2/payment.py msgid "Timeout further payment attempts" -msgstr "" +msgstr "Tiempo de espera para nuevos intentos de pago" #: pretix/plugins/paypal2/payment.py msgid "" "Time duration in minutes after which another payment attempt is possible, " "while the last payment is still under investigation." msgstr "" +"Tiempo, expresado en minutos, transcurrido tras el cual es posible realizar " +"otro intento de pago, mientras el último pago sigue siendo objeto de " +"investigación." #: pretix/plugins/paypal2/payment.py msgid "-- Automatic --" @@ -32188,6 +32121,11 @@ msgid "" "twice in case PayPal allows your initial payment attempt. Please contact us " "to resolve this case." msgstr "" +"PayPal está procesando tu pago. Este proceso está tardando más de lo " +"habitual. Puedes esperar a que PayPal confirme el pago o intentar volver a " +"pagar con este u otro método de pago. Esto podría dar lugar a que se te " +"cobre dos veces en caso de que PayPal acepte tu primer intento de pago. " +"Ponte en contacto con nosotros para resolver este asunto." #: pretix/plugins/paypal2/views.py msgid "" @@ -32439,24 +32377,21 @@ msgid "Base redirection URLs" msgstr "URL de redirección de base" #: pretix/plugins/returnurl/views.py -#, fuzzy -#| msgid "" -#| "Redirection will only be allowed to URLs that start with one of these " -#| "prefixes. Enter one or more allowed URL prefix per line. URL prefixes " -#| "must include a slash after the hostname." msgid "" "Redirection will only be allowed to URLs that start with one of these " "prefixes. Enter one allowed URL prefix per line. URL prefixes must include a " "slash after the hostname." msgstr "" -"La redirección sólo se permitirá a las URL que empiecen por uno de estos " -"prefijos. Introduzca uno o más prefijos de URL permitidos por línea. Los " +"Solo se permitirá la redirección a direcciones URL que empiecen por uno de " +"estos prefijos. Introduce un prefijo de URL permitido por línea. Los " "prefijos de URL deben incluir una barra después del nombre de host." #: pretix/plugins/returnurl/views.py msgid "" "All values must be URLs that include at last one slash after the hostname." msgstr "" +"Todos los valores deben ser direcciones URL que incluyan al menos una barra " +"después del nombre de host." #: pretix/plugins/sendmail/apps.py msgid "Send out emails to all your customers or specific groups of customers." -- 2.54.0 From 19cd0a7f435c4db579220f980a192cc968d382ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2026 18:13:43 +0000 Subject: [PATCH 095/249] Update protobuf requirement from ==7.35.* to ==7.36.* Updates the requirements on [protobuf](https://github.com/protocolbuffers/protobuf) to permit the latest version. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: protobuf dependency-version: 7.36.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 213926155..5e65115d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,7 +79,7 @@ dependencies = [ "phonenumberslite==9.0.*", "Pillow==12.3.*", "pretix-plugin-build", - "protobuf==7.35.*", + "protobuf==7.36.*", "psycopg2-binary", "pycountry", "pycparser==3.0", -- 2.54.0 From 2e8e6a6b0770d88e1782f587a7ea5a55fbbe3095 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Tue, 25 Aug 2026 15:28:15 +0200 Subject: [PATCH 096/249] SBOM creation and upload (#6499) * Build and upload SBOM * Merge SBOMs * Use official cli * Remove matrix * Merge SBOMs to array * Use newer sbom-submit * debug * Remove debug * Explicitly setup node * Add npm location * Add test * REmove test call --- .github/workflows/sbom.yml | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/sbom.yml diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml new file mode 100644 index 000000000..9845bd20e --- /dev/null +++ b/.github/workflows/sbom.yml @@ -0,0 +1,43 @@ +name: SBOM + +on: + push: + branches: [ master, sbom ] + tags: [ 'v.*' ] + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + FORCE_COLOR: 1 + +jobs: + test: + runs-on: ubuntu-22.04 + name: Submission + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Use Node.js + uses: actions/setup-node@v7 + with: + node-version: '24.x' + - uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install system dependencies + run: sudo apt update && sudo apt install -y gettext unzip + - name: Install Python dependencies + run: pip3 install -U "prisma-sbom-submit[python]" + - name: Create SBOM + run: NPM=$(which npm) prisma-sbom-submit collect . sbom.json + - name: Submit SBOM + run: prisma-sbom-submit upload --server https://prisma.pretix.com sbom.json + env: + PRISMA_UPLOAD_TOKEN: ${{ secrets.PRISMA_UPLOAD_TOKEN }} -- 2.54.0 From e6572344ca0c98fb45469d7f59293d8b6097135b Mon Sep 17 00:00:00 2001 From: Lukas Bockstaller Date: Mon, 31 Aug 2026 12:52:06 +0200 Subject: [PATCH 097/249] CI: add tracing for e2e tests during failure (#6491) * collect and upload traces on failure * include deps for pw install --- .github/workflows/tests.yml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a1c1ba297..69a80dcc3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -123,7 +123,24 @@ jobs: working-directory: ./src run: make all compress - name: Install Playwright browsers - run: playwright install + run: playwright install --with-deps - name: Run E2E tests working-directory: ./src - run: PRETIX_CONFIG_FILE=tests/ci_postgres.cfg py.test tests/e2e/ -v --maxfail=10 + run: PRETIX_CONFIG_FILE=tests/ci_postgres.cfg py.test tests/e2e/ -v --maxfail=10 --tracing=retain-on-failure + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-traces + path: test-results/ + - name: Log trace instructions + if: steps.check-traces.outputs.found == 'true' + run: | + { + echo "## 🎭 Playwright traces available" + echo "" + echo "Some tests failed or retried and produced traces." + echo "" + echo "1. Download the **playwright-traces-${{ github.run_id }}** artifact from this run (link in the **Summary** tab, under Artifacts)." + echo "2. Unzip it." + echo "3. Go to https://trace.playwright.dev and drag \`trace.zip\` into the page — or run \`npx playwright show-trace trace.zip\` locally." + } >> "$GITHUB_STEP_SUMMARY" -- 2.54.0 From 023f9104ef17045dde7844a3d965a47dbf9b86da Mon Sep 17 00:00:00 2001 From: luelista Date: Mon, 31 Aug 2026 16:48:48 +0200 Subject: [PATCH 098/249] Fix customer password reset rate limit (#6501) --- src/pretix/presale/forms/customer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pretix/presale/forms/customer.py b/src/pretix/presale/forms/customer.py index 107a7d91a..f301a0380 100644 --- a/src/pretix/presale/forms/customer.py +++ b/src/pretix/presale/forms/customer.py @@ -334,7 +334,7 @@ class ResetPasswordForm(forms.Form): def clean_email(self): if 'email' not in self.cleaned_data: return - if rate_limit("customer_pwreset_check", max_num=10, expire_time=600): + if rate_limit("customer_pwreset_check", include_ip_from_request=self.request, max_num=10, expire_time=600): raise forms.ValidationError( self.error_messages['rate_limit'], code='rate_limit', -- 2.54.0 From 9d53cf840b3c35a60995c39902631abed04f589a Mon Sep 17 00:00:00 2001 From: Lukas Bockstaller Date: Tue, 1 Sep 2026 10:16:51 +0200 Subject: [PATCH 099/249] PayPal: validate that the sale has any captures before marking paid (#6498) * validate that the sale has any captures before marking paid * code style --- src/pretix/plugins/paypal2/views.py | 4 +- src/tests/plugins/paypal2/test_webhook.py | 118 ++++++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/src/pretix/plugins/paypal2/views.py b/src/pretix/plugins/paypal2/views.py index 4ca1b2fb1..e8ff5592e 100644 --- a/src/pretix/plugins/paypal2/views.py +++ b/src/pretix/plugins/paypal2/views.py @@ -494,6 +494,7 @@ def webhook(request, *args, **kwargs): elif payment.state in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED, OrderPayment.PAYMENT_STATE_CANCELED, OrderPayment.PAYMENT_STATE_FAILED): if sale['status'] == 'COMPLETED': + any_captures = False all_captures_completed = True any_pending_review = False any_failed = None @@ -505,6 +506,7 @@ def webhook(request, *args, **kwargs): except ReferencedPayPalObject.MultipleObjectsReturned: pass + any_captures = True if capture['status'] in ('COMPLETED', 'REFUNDED', 'PARTIALLY_REFUNDED'): pass elif capture['status'] in ("DECLINED", "FAILED"): @@ -516,7 +518,7 @@ def webhook(request, *args, **kwargs): any_pending_review = True else: raise ValueError("Unknown paypal capture state: {}".format(capture['status'])) - if all_captures_completed: + if any_captures and all_captures_completed: try: payment.confirm() prov.log_payment_duration(payment) diff --git a/src/tests/plugins/paypal2/test_webhook.py b/src/tests/plugins/paypal2/test_webhook.py index dde4624fd..1b410735c 100644 --- a/src/tests/plugins/paypal2/test_webhook.py +++ b/src/tests/plugins/paypal2/test_webhook.py @@ -299,6 +299,30 @@ def get_test_order_review_pending(): 'method': 'GET'}]} +def get_test_empty_captures(): + return {'id': '806440346Y391300T', + 'intent': 'CAPTURE', + 'status': 'COMPLETED', + 'purchase_units': [{'reference_id': 'default', + 'amount': {'currency_code': 'EUR', 'value': '43.59'}, + 'payee': {'email_address': 'dummy-facilitator@dummy.dummy', + 'merchant_id': 'G6R2B9YXADKWW'}, + 'description': 'Order JWJGC for PayPal v2', + 'custom_id': 'Order PAYPALV2-JWJGC', + 'soft_descriptor': 'MARTINFACIL', + 'payments': {'captures': []} + }], + 'payer': {'name': {'given_name': 'test', 'surname': 'buyer'}, + 'email_address': 'dummy@dummy.dummy', + 'payer_id': 'Q739JNKWH67HE', + 'address': {'country_code': 'DE'}}, + 'create_time': '2022-04-28T11:59:59Z', + 'update_time': '2022-04-28T12:00:22Z', + 'links': [{'href': 'https://api.sandbox.paypal.com/v2/checkout/orders/806440346Y391300T', + 'rel': 'self', + 'method': 'GET'}]} + + class Object(): pass @@ -456,6 +480,100 @@ def test_webhook_all_good(env, client, monkeypatch): assert order.status == Order.STATUS_PAID +@pytest.mark.django_db +def test_webhook_empty_captures(env, client, monkeypatch): + order = env[1] + with scopes_disabled(): + p = order.payments.first() + p.state = OrderPayment.PAYMENT_STATE_PENDING + p.save() + order.status = Order.STATUS_PENDING + order.save() + + pp_order = Result(get_test_empty_captures()) + monkeypatch.setattr("paypalcheckoutsdk.orders.OrdersGetRequest", lambda *args: pp_order) + monkeypatch.setattr("pretix.plugins.paypal2.payment.PaypalMethod.init_api", init_api) + + with scopes_disabled(): + ReferencedPayPalObject.objects.create(order=order, payment=order.payments.first(), + reference="806440346Y391300T") + + client.post('/_paypal/webhook/', json.dumps( + { + "id": "WH-4T867178D0574904F-7TT11736YU643990P", + "create_time": "2022-04-28T12:00:37.077Z", + "resource_type": "checkout-order", + "event_type": "CHECKOUT.ORDER.COMPLETED", + "summary": "Checkout Order Completed", + "resource": { + "update_time": "2022-04-28T12:00:22Z", + "create_time": "2022-04-28T11:59:59Z", + "purchase_units": [ + { + "reference_id": "default", + "amount": { + "currency_code": "EUR", + "value": "43.59" + }, + "payee": { + "email_address": "dummy-facilitator@dummy.dummy", + "merchant_id": "G6R2B9YXADKWW" + }, + "description": "Order JWJGC for PayPal v2", + "custom_id": "Order PAYPALV2-JWJGC", + "soft_descriptor": "MARTINFACIL", + "payments": { + "captures": [] + } + } + ], + "links": [ + { + "href": "https://api.sandbox.paypal.com/v2/checkout/orders/806440346Y391300T", + "rel": "self", + "method": "GET" + } + ], + "id": "806440346Y391300T", + "intent": "CAPTURE", + "payer": { + "name": { + "given_name": "test", + "surname": "buyer" + }, + "email_address": "dummy@dummy.dummy", + "payer_id": "Q739JNKWH67HE", + "address": { + "country_code": "DE" + } + }, + "status": "COMPLETED" + }, + "status": "SUCCESS", + "links": [ + { + "href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4T867178D0574904F-7TT11736YU643990P", + "rel": "self", + "method": "GET", + "encType": "application/json" + }, + { + "href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4T867178D0574904F-7TT11736YU643990P/resend", + "rel": "resend", + "method": "POST", + "encType": "application/json" + } + ], + "event_version": "1.0", + "resource_version": "2.0" + } + ), content_type='application_json') + + order = env[1] + order.refresh_from_db() + assert order.status == Order.STATUS_PENDING + + @pytest.mark.django_db def test_webhook_mark_paid(env, client, monkeypatch): order = env[1] -- 2.54.0 From 4390403b9a4c18867428cd457fdfe4531a0300e3 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Tue, 1 Sep 2026 10:21:20 +0200 Subject: [PATCH 100/249] Add label-overrides to contact_form_fields_overrides and question_form_fields_overrides (Z#23244154) (#6504) --- src/pretix/base/views/mixins.py | 2 ++ src/pretix/presale/checkoutflow.py | 4 ++++ src/pretix/presale/signals.py | 8 ++++---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pretix/base/views/mixins.py b/src/pretix/base/views/mixins.py index 157f80543..386e991ee 100644 --- a/src/pretix/base/views/mixins.py +++ b/src/pretix/base/views/mixins.py @@ -135,6 +135,8 @@ class BaseQuestionsViewMixin: question_field.initial = getattr(question_field, 'initial', None) or src['initial'] if 'validators' in src: question_field.validators += src['validators'] + if 'label' in src: + question_field.label = src['label'] if len(form.fields) > 0: formlist.append(form) diff --git a/src/pretix/presale/checkoutflow.py b/src/pretix/presale/checkoutflow.py index 0507932a5..a4981316c 100644 --- a/src/pretix/presale/checkoutflow.py +++ b/src/pretix/presale/checkoutflow.py @@ -840,6 +840,8 @@ class QuestionsStep(CartQuestionsViewMixin, CartMixin, TemplateFlowStep): f.fields[fname].disabled = val['disabled'] if 'validators' in val and fname in f.fields: f.fields[fname].validators += val['validators'] + if 'label' in val and fname in f.fields: + f.fields[fname].label = val['label'] return f @@ -944,6 +946,8 @@ class QuestionsStep(CartQuestionsViewMixin, CartMixin, TemplateFlowStep): f.fields[fname].disabled = val['disabled'] if 'validators' in val and fname in f.fields: f.fields[fname].validators += val['validators'] + if 'label' in val and fname in f.fields: + f.fields[fname].label = val['label'] return f diff --git a/src/pretix/presale/signals.py b/src/pretix/presale/signals.py index 007f9f459..3eeea867c 100644 --- a/src/pretix/presale/signals.py +++ b/src/pretix/presale/signals.py @@ -233,7 +233,7 @@ Arguments: ``request``, ``order`` This signal allows you to override fields of the contact form that is presented during checkout and by default only asks for the email address. It is also being used for the invoice address form. You are supposed to return a dictionary of dictionaries with globally unique keys. The -value-dictionary should contain one or more of the following keys: ``initial``, ``disabled``, +value-dictionary should contain one or more of the following keys: ``label``, ``initial``, ``disabled``, ``validators``. The key of the dictionary should be the name of the form field. As with all event plugin signals, the ``sender`` keyword argument will contain the event. A ``request`` @@ -264,9 +264,9 @@ Arguments: ``position``, ``request`` This signal allows you to override fields of the questions form that is presented during checkout and by default only asks for the questions configured in the backend. You are supposed to return a dictionary of dictionaries with globally unique keys. The value-dictionary should contain one or -more of the following keys: ``initial``, ``disabled``, ``validators``. The key of the dictionary -should be the form field name for system fields (e.g. ``company``), or the question's ``identifier`` -for user-defined questions. +more of the following keys: ``label``, ``initial``, ``disabled``, ``validators``. The key of the +dictionary should be the form field name for system fields (e.g. ``company``), or the question's +``identifier`` for user-defined questions. The ``position`` keyword argument will contain a ``CartPosition`` or ``OrderPosition`` object. -- 2.54.0 From a84a02c298c3dc55862bec71b2fc8d28ca00ad14 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Tue, 1 Sep 2026 11:22:19 +0200 Subject: [PATCH 101/249] Vite: Add dev CORS (#6511) --- vite.config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vite.config.ts b/vite.config.ts index b0578d76c..1bcace40a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -31,6 +31,9 @@ export default defineConfig({ // Allow serving source files from sibling plugin directories allow: ['src', ...pluginDirs], }, + cors: { + origin: /^https?:\/\/(?:(?:[^:]+\.)?localhost|127\.0\.0\.1|\[::1\]|[^:]+\.pretix\.(dev|work))(?::\d+)?$/ + }, }, build: { manifest: true, -- 2.54.0 From 58a58eff8379e227684f651731e4edc71477921b Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Tue, 1 Sep 2026 13:19:32 +0200 Subject: [PATCH 102/249] Widget: fix show taxline for 0 or 0.00 tax-rate (#6502) --- src/pretix/static/pretixpresale/js/widget/widget.js | 6 +++++- .../static/pretixpresale/widget/src/components/PriceBox.vue | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/pretix/static/pretixpresale/js/widget/widget.js b/src/pretix/static/pretixpresale/js/widget/widget.js index 9c25ba3a3..fd4f3338b 100644 --- a/src/pretix/static/pretixpresale/js/widget/widget.js +++ b/src/pretix/static/pretixpresale/js/widget/widget.js @@ -345,7 +345,7 @@ Vue.component('pricebox', { + ' :min="display_price_nonlocalized" :value="suggested_price_nonlocalized" :name="field_name"' + ' step="any" v-bind:aria-labelledby="aria_labelledby" v-bind:aria-describedby="price_desc_id">' + '
' - + '' + + '' + '{{ taxline }}' + '' + ''), @@ -422,6 +422,10 @@ Vue.component('pricebox', { return '' + this.$root.currency + " " + this.display_price; } }, + show_taxline: function () { + // rate can either be "0.00" or "0" => parseFloat to check + return Number.parseFloat(this.price.rate) && Number.parseFloat(this.price.gross); + }, taxline: function () { if (this.$root.display_net_prices) { if (this.price.includes_mixed_tax_rate) { diff --git a/src/pretix/static/pretixpresale/widget/src/components/PriceBox.vue b/src/pretix/static/pretixpresale/widget/src/components/PriceBox.vue index 4b0d78b01..ef2733960 100644 --- a/src/pretix/static/pretixpresale/widget/src/components/PriceBox.vue +++ b/src/pretix/static/pretixpresale/widget/src/components/PriceBox.vue @@ -86,7 +86,8 @@ const taxline = computed(() => { } }) -const showTaxline = computed(() => props.price.rate !== '0' && props.price.gross !== '0.00') +// rate can either be "0.00" or "0" => parseFloat to check +const showTaxline = computed(() => Number.parseFloat(props.price.rate) && Number.parseFloat(props.price.gross))