Compare commits

..
Author SHA1 Message Date
Mira Weller 54726d6fcd TODO markings 2026-09-15 16:37:23 +02:00
Mira Weller 8e3cc12357 rebase migration and migrate order-level questions as well 2026-09-15 16:37:23 +02:00
Mira Weller 53a8a7954d QuestionnaireChild: system_question und user_question in system_datafield und user_datafield umbenennen 2026-09-15 16:37:22 +02:00
Mira Weller 59cebf3ee8 Rebased --- Squashed commit of the following:
commit 2177bebf6a
Merge: ea613bf271 d08216d8c5
Author: Mira Weller <weller@rami.io>
Date:   Fri Aug 7 21:22:31 2026 +0200

    Merge branch 'master' into questions-vue3

commit ea613bf271
Author: Mira Weller <weller@rami.io>
Date:   Fri Aug 7 21:21:35 2026 +0200

    wip

commit c8bed5e3b9
Author: Mira Weller <weller@rami.io>
Date:   Fri Aug 7 10:15:00 2026 +0200

    rename get_cart to get_cart_positions (keep old name alive)

commit 1ec27fff05
Author: Mira Weller <weller@rami.io>
Date:   Fri Aug 7 10:01:38 2026 +0200

    filter questionnaires by sales channel

commit 0294f205d0
Author: Mira Weller <weller@rami.io>
Date:   Thu Aug 6 20:06:05 2026 +0200

    refactor: extract build_requested_valid_from_field method

commit 3424aacdc3
Author: Mira Weller <weller@rami.io>
Date:   Thu Aug 6 20:05:44 2026 +0200

    rename presale question forms and mixins

commit 4012889076
Author: Mira Weller <weller@rami.io>
Date:   Thu Aug 6 19:42:29 2026 +0200

    rebase migration

commit fe15d71cd2
Merge: 3ea7ea31b6 4d9dfa88fe
Author: Mira Weller <weller@rami.io>
Date:   Thu Aug 6 19:25:44 2026 +0200

    Merge branch 'master' into questions-vue3

    # Conflicts:
    #	src/pretix/base/forms/questions.py
    #	src/pretix/base/middleware.py
    #	src/pretix/base/templates/pretixbase/redirect.html

commit 3ea7ea31b6
Author: Mira Weller <weller@rami.io>
Date:   Fri Jul 24 17:37:46 2026 +0200

    wip

commit 5b34a25fb5
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 16:21:11 2026 +0200

    use questionnaire data for rendering checkout questions step

commit 9467f1d08d
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 15:54:14 2026 +0200

    refactor: extract build_system_question_field method

commit 85344a7690
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 15:50:41 2026 +0200

    refactor: extract build_user_question_field method

commit 3b47f4aaa7
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 15:49:34 2026 +0200

    refactor: cartpos / orderpos

commit 0309b05121
Author: Mira Weller <weller@rami.io>
Date:   Thu Mar 19 13:52:18 2026 +0100

    vite config: allow cors access from pretix.work subdomains

commit ad092ae0d1
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 12:35:33 2026 +0200

    re-add vue-slicksort dep

commit 9f42d679cc
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 12:28:53 2026 +0200

    rebase migration

commit 9eae9c6de7
Merge: 84c51e031e 694b915d89
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 12:24:49 2026 +0200

    Merge branch 'master' into questions-vue3

    # Conflicts:
    #	.github/workflows/tests.yml
    #	.gitignore
    #	Dockerfile
    #	package-lock.json
    #	package.json
    #	src/pretix/base/management/commands/runserver.py
    #	src/pretix/base/middleware.py
    #	src/pretix/base/templatetags/vite.py
    #	src/pretix/control/forms/global_settings.py
    #	src/pretix/control/templates/pretixcontrol/checkin/list_edit.html
    #	src/pretix/control/views/item.py
    #	src/pretix/presale/views/widget.py
    #	src/pretix/settings.py
    #	src/pretix/static/pretixcontrol/js/ui/checkinrules/App.vue
    #	src/pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts
    #	src/pretix/static/pretixcontrol/js/ui/checkinrules/django-interop.ts
    #	src/pretix/static/pretixcontrol/js/ui/checkinrules/lookup-select2.vue
    #	src/pretix/static/pretixcontrol/js/ui/checkinrules/timefield.vue
    #	src/pretix/static/pretixcontrol/js/ui/checkinrules/viz-node.vue
    #	src/pretix/static/pretixpresale/widget/index.html
    #	src/pretix/static/pretixpresale/widget/src/api.ts
    #	src/pretix/static/pretixpresale/widget/src/button.ts
    #	src/pretix/static/pretixpresale/widget/src/components/PriceBox.vue
    #	src/pretix/static/pretixpresale/widget/src/main.ts
    #	src/pretix/static/pretixpresale/widget/src/sharedStore.ts
    #	src/pretix/static/pretixpresale/widget/src/utils.ts
    #	src/pretix/static/pretixpresale/widget/src/widget.ts
    #	src/tests/e2e/conftest.py
    #	vite.config.ts

commit 84c51e031e
Merge: 902ee9242e 222b453b43
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 12:20:05 2026 +0200

    Merge branch 'master' into questions-vue3

commit 902ee9242e
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 12:19:26 2026 +0200

    css comment

commit b758efd4a9
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 12:19:19 2026 +0200

    question dependencies

commit c68fe00502
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 12:18:00 2026 +0200

    add data field edit link

commit b7da9bb6c1
Author: Mira Weller <weller@rami.io>
Date:   Wed Jun 17 12:16:20 2026 +0200

    handle hidden questions

commit 897120d3da
Author: Mira Weller <weller@rami.io>
Date:   Tue May 12 13:04:08 2026 +0200

    add ts model definitions

commit 09abc5725e
Author: Mira Weller <weller@rami.io>
Date:   Tue May 12 11:30:12 2026 +0200

    reorder and save

commit 9a7d5abb1f
Author: Mira Weller <weller@rami.io>
Date:   Tue May 12 11:29:37 2026 +0200

    fix unhandled exception in taxrules API

commit 3cee5f524b
Author: Mira Weller <weller@rami.io>
Date:   Tue May 5 18:12:21 2026 +0200

    use slicksort for reordering questionnaires and questions

commit 3a1a6b988e
Author: Mira Weller <weller@rami.io>
Date:   Tue Apr 28 19:23:35 2026 +0200

    improve i18n, implement "add text block" dialog

commit bdc720f5a2
Author: Mira Weller <weller@rami.io>
Date:   Fri Mar 27 18:57:22 2026 +0100

    wip

commit 583184af38
Author: Mira Weller <weller@rami.io>
Date:   Thu Mar 19 21:45:32 2026 +0100

    change existing views

commit 63d9f7cea8
Author: Mira Weller <weller@rami.io>
Date:   Thu Mar 19 21:45:32 2026 +0100

    adapt vue app to new data model (wip)

commit c3d6fb1bd6
Author: Mira Weller <weller@rami.io>
Date:   Thu Mar 19 21:44:47 2026 +0100

    add Questionnaire and QuestionnaireChild models and their API; migrate existing data

commit bb43acd257
Author: Mira Weller <weller@rami.io>
Date:   Thu Mar 19 13:52:28 2026 +0100

    vite config: add questionnaires app

commit e0744951e8
Author: Mira Weller <weller@rami.io>
Date:   Thu Mar 19 13:52:18 2026 +0100

    vite config: allow cors access from pretix.work subdomains

commit fef1e356f7
Author: Mira Weller <weller@rami.io>
Date:   Thu Mar 19 13:33:24 2026 +0100

    fix out-of-the-box experience

commit 81775f5d2d
Author: Mira Weller <weller@rami.io>
Date:   Thu Mar 19 13:33:24 2026 +0100

    setup questionnaires vue app, add code from question editor proof of concept

commit 4bbcc398cc
Merge: a924d0a266 58840a5fd6
Author: Mira Weller <weller@rami.io>
Date:   Thu Mar 19 13:13:32 2026 +0100

    Merge remote-tracking branch 'refs/remotes/origin/master' into questions-vue3
2026-09-15 16:37:22 +02:00
Richard Schreiber 3648ef28d7 Fix deanonymizing emails with multiple dots (#6559) 2026-09-15 13:16:36 +02:00
Richard Schreiber 689b99c3aa PDF: fix inline merge_background on fg_pdf instead of its own PdfWriter (#6555)
* PDF: fix inline merge_background on fg_pdf instead of its own PdfWriter

* fix pdf_header

* fix flake8
2026-09-14 15:45:54 +02:00
Raphael Michel 6c40daa863 Hotfix for JS issue when submit() is called from JS 2026-09-14 15:33:25 +02:00
dependabot[bot]andRaphael Michel efdc83f72a Update pypdf requirement from ==6.5.* to ==6.11.* (#6176)
* Update pypdf requirement from ==6.5.* to ==6.11.*

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

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

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

* Try to adjust code

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Raphael Michel <michel@rami.io>
2026-09-14 14:09:28 +02:00
dependabot[bot] 8b10534c74 Bump smol-toml from 1.6.1 to 1.7.1 (#6546)
Bumps [smol-toml](https://github.com/squirrelchat/smol-toml) from 1.6.1 to 1.7.1.
- [Release notes](https://github.com/squirrelchat/smol-toml/releases)
- [Commits](https://github.com/squirrelchat/smol-toml/compare/v1.6.1...v1.7.1)

---
updated-dependencies:
- dependency-name: smol-toml
  dependency-version: 1.7.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-14 10:31:12 +02:00
Raphael Michel bcacff2ee7 Voucher list: Add budget and budget used to CSV export (Z#23237857) (#6520)
* Voucher list: Add budget and budget used to CSV export (Z#23237857)

* Fix failing test
2026-09-14 10:31:07 +02:00
Kara Engelhardt 47096f7f1b sendmail: fix query performance when filtering on checkin (Z#23244045) 2026-09-14 10:24:58 +02:00
Raphael Michel c240ebcb8e Force Django update 2026-09-14 09:51:32 +02:00
Raphael Michel ce3e59e020 PDF reports: Fix headers for overlong event names (Z#23239566) (#6521) 2026-09-14 09:38:44 +02:00
Raphael Michel 4a9da40028 [A11y] Form JavaScript: Support for <button formnovalidate> (#6536) 2026-09-14 09:38:34 +02:00
Raphael Michelandpajowu eab1efd5f4 Phone number input: Fix workaround for prefixes shared between countries (fixes #6544) (#6545)
* Phone number input: Fix workaround for prefixes shared between countries (fixes #6544)

* Update src/pretix/base/forms/questions.py

Co-authored-by: pajowu <engelhardt@pretix.eu>

---------

Co-authored-by: pajowu <engelhardt@pretix.eu>
2026-09-14 09:35:32 +02:00
Richard Schreiber c7290b614e Show payment-QR-codes on mobile (#6503) 2026-09-14 08:50:45 +02:00
Raphael Michel 1aea5633eb Discounts: Fix ManyToMany fields when cloning (#6547) 2026-09-11 18:35:37 +02:00
Richard Schreiber 86cc719651 Widget: serve vite by default, origin-whitelist for vue2 (#6403) 2026-09-11 08:53:42 +02:00
Mira Weller 742a69f09f Add git-blame-ignore-revs file (see #6541) 2026-09-10 12:54:51 +02:00
Yasunobu YesNo Kawaguchi c05dee7af5 Translations: Update Japanese
Currently translated at 100.0% (6419 of 6419 strings)

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

powered by weblate
2026-09-10 12:36:49 +02:00
Albizuri e399a49c8b Translations: Update Basque
Currently translated at 48.0% (3086 of 6419 strings)

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

powered by weblate
2026-09-10 12:36:49 +02:00
Linnea Thelander b9b951e56b Translations: Update Swedish
Currently translated at 98.0% (255 of 260 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/sv/

powered by weblate
2026-09-10 12:36:49 +02:00
Linnea Thelander 88bb1f4008 Translations: Update Swedish
Currently translated at 85.8% (5509 of 6419 strings)

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

powered by weblate
2026-09-10 12:36:49 +02:00
Renne Rocha 24e7db7ddd Translations: Update Portuguese (Brazil)
Currently translated at 92.4% (5937 of 6419 strings)

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

powered by weblate
2026-09-10 12:36:49 +02:00
Hijiri Umemoto 96ed6fe1a6 Translations: Update Japanese
Currently translated at 100.0% (6419 of 6419 strings)

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

powered by weblate
2026-09-10 12:36:49 +02:00
Raphael MichelandKara Engelhardt d85e52c83e ESlint: Ignore vendored and pre-vue code (#6541)
* ESlint: Ignore vendored and pre-vue code

* Format pre-vue code with eslint where possible

---------

Co-authored-by: Kara Engelhardt <engelhardt@pretix.eu>
2026-09-10 12:35:10 +02:00
Raphael Michel d9b5fa5b16 Fix incomplete warning message 2026-09-10 12:16:10 +02:00
Kara Engelhardt fb948cc7f4 sendmail: small performance winds in checkin query 2026-09-10 11:17:32 +02:00
Kara Engelhardt c358f412fd sendmail: code cleanup 2026-09-10 11:17:32 +02:00
Kara Engelhardt b78de6386b sendmail: fix quadratic checkin query 2026-09-10 11:17:32 +02:00
Raphael MichelandRichard Schreiber 50c00b47ae Event index: Add subevent time and name to <title> (Z#23246174) (#6542)
* Event index: Add subevent time and name to <title> (Z#23246174)

* Update src/pretix/presale/templates/pretixpresale/event/index.html

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

---------

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
2026-09-10 10:05:55 +02:00
Ferdinand Malcher 6025c7942f Product pictures: fix CSS to always fit image to viewport (#6527) 2026-09-09 14:10:15 +02:00
dependabot[bot] 23a3412154 Update django-statici18n requirement from ==2.7.* to ==2.8.* (#6529)
Updates the requirements on [django-statici18n](https://github.com/zyegfryed/django-statici18n) to permit the latest version.
- [Changelog](https://github.com/zyegfryed/django-statici18n/blob/main/docs/changelog.rst)
- [Commits](https://github.com/zyegfryed/django-statici18n/compare/v2.7.0...v2.8.0)

---
updated-dependencies:
- dependency-name: django-statici18n
  dependency-version: 2.8.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-09 14:08:54 +02:00
Raphael MichelandRichard Schreiber 3635f6fb0c Widget: Add data-fix equivalent for attendee data (Z#23202714) (#6533)
* Widget: Add data-fix equivalent for attendee data (Z#23202714)

* Update src/pretix/presale/checkoutflow.py

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* Update src/pretix/presale/checkoutflow.py

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* Update src/pretix/presale/checkoutflow.py

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

---------

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
2026-09-09 14:06:41 +02:00
Kara Engelhardt 2aadfa2ce4 RRuleForm: allow selecting the fourth occurence of a weekday of a month (Z#23245893)
The fourth often is the same as the last, but not always. A weekday can appear five times in a month
2026-09-09 12:21:42 +02:00
Raphael Michel dd0d78242e PDF editor: Harden cached file access for background (Z#23242291) (#6446) 2026-09-09 11:13:29 +02:00
Raphael Michelandluelista 42c5de895c CachedFileField: Bind file access to session (Z#23242301) (#6447)
* CachedFileField: Bind file access to session (Z#23242301)

* Apply suggestion from @luelista

Co-authored-by: luelista <weller@rami.io>

---------

Co-authored-by: luelista <weller@rami.io>
2026-09-09 09:52:40 +02:00
Raphael Michelandluelista e91718e73b Password reset: Prevent race condition that uses token twice (Z#23242132) (#6448)
* Password reset: Prevent race condition that uses token twice

* Apply suggestion from @luelista

Co-authored-by: luelista <weller@rami.io>

---------

Co-authored-by: luelista <weller@rami.io>
2026-09-09 09:52:38 +02:00
Martin GrossandRaphael Michel 2c15d8b074 Event Settings: Make contact_url and imprint_url I18nURLField (Z#23238623) (#6506)
* Event Settings: Make contact_url and imprint_url I18nURLField (Z#23238623)

* isort

* Update doc/api/resources/events.rst

---------

Co-authored-by: Raphael Michel <michel@pretix.eu>
2026-09-09 09:14:37 +02:00
Raphael MichelandLukas Bockstaller edb4069e18 Payment step: Allow to postpone payment choice on some sales channels (#6516)
* Payment step: Allow to postpone payment choice on some sales channels

* Add tests

* handle payment provider (de-)selection and partial payments (#6526)

---------

Co-authored-by: Lukas Bockstaller <bockstaller@pretix.eu>
2026-09-08 09:32:15 +02:00
Raphael MichelandRichard Schreiber dc7d5c6029 Event calendar: consider events without products "not available" for filtering (#6515)
* Event calendar: consider events without products "not available" for filtering

Also, drop the help text of the flag that is no longer accurate.

* Update src/pretix/presale/views/organizer.py

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

---------

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
2026-09-04 16:29:46 +02:00
Raphael Michel caa6fb187b VAT ID validation: Alternative API for German shops (#6507) 2026-09-02 17:48:38 +02:00
Lukas Bockstaller 7d93cae2a9 set the _required attribute on the ModelMultipleChoiceField (#6514)
setting _required forces the CheckoutFieldRenderer to add the "required" bit to the label.
We only need to remove it from the widget attrs to avoid the html input field validation that would force us to check every single box
2026-09-02 10:35:19 +02:00
Richard Schreiber 58a58eff83 Widget: fix show taxline for 0 or 0.00 tax-rate (#6502) 2026-09-01 13:19:32 +02:00
Martin Gross a84a02c298 Vite: Add dev CORS (#6511) 2026-09-01 11:22:19 +02:00
Martin Gross 4390403b9a Add label-overrides to contact_form_fields_overrides and question_form_fields_overrides (Z#23244154) (#6504) 2026-09-01 10:21:20 +02:00
Lukas Bockstaller 9d53cf840b PayPal: validate that the sale has any captures before marking paid (#6498)
* validate that the sale has any captures before marking paid

* code style
2026-09-01 10:16:51 +02:00
luelista 023f9104ef Fix customer password reset rate limit (#6501) 2026-08-31 16:48:48 +02:00
Lukas Bockstaller e6572344ca CI: add tracing for e2e tests during failure (#6491)
* collect and upload traces on failure

* include deps for pw install
2026-08-31 12:52:06 +02:00
Raphael Michel 2e8e6a6b07 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
2026-08-25 15:28:15 +02:00
dependabot[bot] 19cd0a7f43 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] <support@github.com>
2026-08-25 14:37:23 +02:00
CVZ-es aa583a291f 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
2026-08-25 14:33:41 +02:00
CVZ-es 1414c22eb6 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
2026-08-25 14:33:41 +02:00
Raphael Michel 0a6b8c0493 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
2026-08-25 14:33:41 +02:00
Raphael Michel 4b4a301e6e 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
2026-08-25 14:33:41 +02:00
Richard Schreiber d7aae65777 API: fix tax rules create default handling (#6490)
* API: fix tax rules create default handling

* fix flake8
2026-08-25 09:39:21 +02:00
Richard Schreiber dbd971cc22 API: fix writing old permissions on teams (#6489) 2026-08-25 09:38:11 +02:00
Richard Schreiber 6d4aba6e3d Questions: add min-length to string/text type questions (#6488)
* Questions: add min-length to string/text type questions

* fix test
2026-08-25 09:37:37 +02:00
Raphael Michel 4755e7af0e Translations: Update word list 2026-08-24 17:49:33 +02:00
Raphael Michel f476ce7696 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@pretix.eu>
2026-08-24 17:36:00 +02:00
Raphael Michel bf85789efd 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
2026-08-24 17:35:25 +02:00
Raphael Michel 3fbd63e80a 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
2026-08-24 17:35:25 +02:00
Raphael Michel 2af60b499f Fix typos and extend wordlist 2026-08-24 17:35:17 +02:00
Raphael Michel b74371bef1 Fix typos 2026-08-24 17:26:37 +02:00
Raphael Michel 9f8ba28cda Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@pretix.eu>
2026-08-24 17:12:24 +02:00
Raphael Michel 44a7bfceab Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@pretix.eu>
2026-08-24 16:39:26 +02:00
Albizuri 5af9be6ef0 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
2026-08-24 16:38:54 +02:00
b809d93bdc Do not hide subevent list if filtered list is empty (#6460)
* Do not hide subevent list if filtered list is empty

* Event calendar: Allow to show a message if no events are found

* Apply suggestion from @luelista

Co-authored-by: luelista <weller@rami.io>

* Fix failures on org level

* add aria-hidden if no subevents

* Update src/pretix/base/settings.py

* Update src/pretix/base/settings.py

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

---------

Co-authored-by: luelista <weller@rami.io>
Co-authored-by: Richard Schreiber <schreiber@rami.io>
Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
2026-08-24 14:42:41 +02:00
Raphael Michel d5a8d71ed3 Sentry: enable_logs is removed 2026-08-24 14:42:15 +02:00
dependabot[bot] d481574d42 Update sentry-sdk requirement from ==2.66.* to ==2.68.*
Updates the requirements on [sentry-sdk](https://github.com/getsentry/sentry-python) to permit the latest version.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.66.0...2.68.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-version: 2.68.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-24 14:42:15 +02:00
Kara Engelhardt 0593172146 Transmit invoices generated due to paymentprovider changes (Z#23242806) 2026-08-24 13:45:54 +02:00
Translate pretix user 586 64a5361dba Translations: Update Italian
Currently translated at 60.8% (3887 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 5622c1ddaa Translations: Update Italian
Currently translated at 60.7% (3877 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 73c29fc3b9 Translations: Update Italian
Currently translated at 60.1% (3842 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 c73f2f77cf Translations: Update Italian
Currently translated at 59.2% (3787 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 55a0db3a32 Translations: Update Italian
Currently translated at 57.7% (3687 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 a47768312b Translations: Update Italian
Currently translated at 57.5% (3676 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Xiaofan Wang cf272bf575 Translations: Update Chinese (Simplified Han script)
Currently translated at 44.7% (2861 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Hijiri Umemoto f3e0e7f4ac Translations: Update Japanese
Currently translated at 100.0% (6387 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 5a06f9416c Translations: Update Italian
Currently translated at 53.0% (3387 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 606e3f1c0f 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
2026-08-24 13:33:21 +02:00
Translate pretix user 586 e38ae32028 Translations: Update Italian
Currently translated at 51.4% (3287 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 b74e2fa3e1 Translations: Update Italian
Currently translated at 48.3% (3087 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 c79a7002a6 Translations: Update Italian
Currently translated at 45.5% (2907 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Xiaofan Wang 1ebbae2b5b Translations: Update Chinese (Simplified Han script)
Currently translated at 44.7% (2859 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 7db3b47889 Translations: Update Italian
Currently translated at 43.3% (2767 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
IGnacy-NorthVan b7ac8fc4ef Translations: Update Polish
Currently translated at 89.3% (5706 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Hijiri Umemoto c1a830e54d Translations: Update Japanese
Currently translated at 100.0% (6387 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Hijiri Umemoto 2178dee6db Translations: Update French
Currently translated at 99.9% (6382 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 266104f4af Translations: Update Italian
Currently translated at 42.5% (2720 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Translate pretix user 586 d10afbd080 Translations: Update Italian
Currently translated at 40.3% (2579 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Hijiri Umemoto 1e78bfbc4c Translations: Update Chinese (Traditional Han script)
Currently translated at 88.4% (5647 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Hijiri Umemoto 08356feea1 Translations: Update Japanese
Currently translated at 100.0% (6387 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Julien b124cb6aad Translations: Update Swedish
Currently translated at 98.4% (256 of 260 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/sv/

powered by weblate
2026-08-24 13:33:21 +02:00
Julien cb5182f21c Translations: Update Swedish
Currently translated at 86.2% (5510 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Julien 011487404c Translations: Update French
Currently translated at 100.0% (260 of 260 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/fr/

powered by weblate
2026-08-24 13:33:21 +02:00
Julien 7703c04954 Translations: Update French
Currently translated at 99.8% (6380 of 6387 strings)

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

powered by weblate
2026-08-24 13:33:21 +02:00
Raphael Michelandluelista 58f331ba1f Allow to set payment term per sales channel (#6459)
* Allow to set payment term per sales channel

* Apply suggestion from @luelista

Co-authored-by: luelista <weller@rami.io>

---------

Co-authored-by: luelista <weller@rami.io>
2026-08-24 09:09:47 +02:00
Martin Gross 91a3993cef Seating: Set subevents to None if event has no subevents for _seat_allowed annotation (Z#23243880) (#6487) 2026-08-19 14:12:05 +02:00
pajowuandRichard Schreiber 4969c12c0a Fix widget not regenerating (#6474)
* Fix widget not regenerating

Adds support for use_vite variant to updateassets management command and unifies widget generation code

* Simplify DEBUG-mode handling

* fix should_save if file does not exist

---------

Co-authored-by: Richard Schreiber <schreiber@rami.io>
2026-08-19 09:14:24 +02:00
Lukas Bockstaller 0b319d201c handle captures that are declined (PRETIXEU-F8X & PRETIXEU-F93) (#6482)
* handle captures that are declined

* handle failed captures not stored in sale

* further fixes

* fix tests
2026-08-19 08:41:25 +02:00
Richard Schreiber eceeb9caa7 Widget: fix missing item-id in pricebox (Z#23243324) (#6485) 2026-08-18 16:29:33 +02:00
Richard Schreiber 3c17d05d05 Widget: fix hiding taxline when rate is 0 (Z#23243324) (#6486) 2026-08-18 16:19:36 +02:00
Richard Schreiber 1c17c35b16 Add event_begin_time & event_end_time to scheduledmail templates (Z#23241932) (#6484) 2026-08-18 16:19:15 +02:00
Richard Schreiber f9ea9de078 Fix sendmail/scheduled-mails preview event-based placeholders
* Fix sendmail/scheduled-mails placeholders

* Add event_or_subevent to context

* Add placeholders to tests

* fix and improve test

* Revert "fix and improve test"

This reverts commit 874a11e982.

* undo placeholder-tests

* add test
2026-08-18 14:54:19 +02:00
luelista 4dd6e1a19f Fix attendee addresses for country requiring state in address (#6480) 2026-08-14 17:06:41 +02:00
Richard Schreiber 4cb9d76281 Fix typo in event’s default download reminder attendee (#6473) 2026-08-14 16:47:39 +02:00
luelistaandRichard Schreiber f8cc31b120 Order-level questions (#6471)
* New CheckoutSession model, created and deleted throught cart lifetime but only used for order-level question answers so far
* Order-level QuestionAnswers (relations to CheckoutSession / Order)
* New container_type field on Question model to specify whether Question belongs to order or orderposition
* Order-level questions are currently experimental, UI is hidden behind feature flag

---------

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
2026-08-14 14:56:45 +02:00
pajowuandRichard Schreiber b241adb7d0 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 <schreiber@rami.io>
2026-08-14 12:34:55 +02:00
Martin Gross 7b1558b22e Revert "Stripe/Wero: Do not send payment_method_data"
This reverts commit 488f731396.
2026-08-14 12:07:34 +02:00
Martin Gross 488f731396 Stripe/Wero: Do not send payment_method_data 2026-08-14 11:24:34 +02:00
Lukas Bockstaller b8b02de283 paypal change default for allow_retries_during_compliance_hold 2026-08-13 18:10:38 +02:00
Lukas Bockstallerandpajowu c4a5a9a84d 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 <pajowu@pajowu.de>

* check all capture elements

* rename method and change defaults

* remove left over Constant

* flake8 .

---------

Co-authored-by: pajowu <pajowu@pajowu.de>
2026-08-13 17:28:54 +02:00
Kara Engelhardt 4e5fbacf6d Replace deprecated redis setex call 2026-08-13 16:59:42 +02:00
7fe31634e6 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 <michel@pretix.eu>

* Apply suggestions from code review

Co-authored-by: Raphael Michel <mail@raphaelmichel.de>

* 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 <mail@raphaelmichel.de>

* 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 <mail@raphaelmichel.de>

* remove empty docstrings

* add depreciation warning

* change event listener to pretix:bind-forms

---------

Co-authored-by: Raphael Michel <michel@pretix.eu>
Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
2026-08-12 15:37:29 +02:00
Raphael MichelandRichard Schreiber f25c233e91 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 <schreiber@pretix.eu>

* Fixes after review

* Fix check in loop

---------

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
2026-08-10 16:13:54 +02:00
robbi5 5cf28f1b81 Fix typo in devicesecurity blocking checkinrpc.annul (#6467) 2026-08-10 15:58:15 +02:00
Raphael Michel d08216d8c5 API: Allow to simulate check-ins (#6360)
* API: Allow to simulate check-ins

* Add missing file
2026-08-07 18:59:01 +02:00
Lukas Bockstaller 4a28689690 log paypal payment durations (#6461)
* log payment processing durations

* remove log noise

* fix attribute access

* remove debugging import
2026-08-07 16:34:36 +02:00
luelista 958f75b109 Add tests to prevent reintroducing CSP nonces (Z#23240534) (#6409)
As discussed in PR #6387
2026-08-07 14:36:46 +02:00
Richard Schreiber 81f58456e5 Fix API-docs example for addon_to on order-change (#6451)
* Fix API-docs example for addon_to on order-change

* Update orders.rst
2026-08-07 11:03:36 +02:00
Lukas BockstallerandPhin Wolkwitz 4d9dfa88fe 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 <wolkwitz@pretix.eu>

* add test

* cleanup logic regarding uninteresting resource_type

* store payment.info during _execute_payment asap

---------

Co-authored-by: Phin Wolkwitz <wolkwitz@pretix.eu>
2026-08-06 15:06:20 +02:00
Raphael Michel ea792e76b2 Device list: Show online status (#6455)
* Device list: Show online status

* Fixes post-review
2026-08-06 09:07:20 +02:00
Raphael Michel 5c448279f0 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.
2026-08-05 16:52:28 +02:00
282 changed files with 35788 additions and 19061 deletions
+2
View File
@@ -0,0 +1,2 @@
# Format pre-vue code with eslint where possible (2026-09-10)
d85e52c83ed3639e040372fd0052e842e4899d90
+43
View File
@@ -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 }}
+19 -2
View File
@@ -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
View File
@@ -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**:
+9 -7
View File
@@ -566,7 +566,7 @@ organizer level.
Content-Type: application/json
{
"imprint_url": "https://pretix.eu",
"region": "DE",
}
@@ -579,12 +579,14 @@ organizer level.
Content-Type: application/json
{
"imprint_url":
"region":
{
"value": "https://pretix.eu",
"label": "Imprint URL",
"value": "DE",
"label": "Region",
"readonly": false,
"help_text": "This should point e.g. to a part of your website that has your contact details and legal information."
"help_text": "Will be used to determine date and time formatting as well as default country for customer
addresses and phone numbers. For formatting, this takes less priority than the language and
is therefore mostly relevant for languages used in different regions globally (like English)."
}
},
@@ -620,7 +622,7 @@ organizer level.
Content-Type: application/json
{
"imprint_url": "https://example.org/imprint/"
"region": "DE"
}
**Example response**:
@@ -632,7 +634,7 @@ organizer level.
Content-Type: application/json
{
"imprint_url": "https://example.org/imprint/",
"region": "DE",
}
+2 -2
View File
@@ -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",
}
],
+5
View File
@@ -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": [
{
+58 -1
View File
@@ -8,7 +8,64 @@ import vuePug from 'eslint-plugin-vue-pug'
const ignores = globalIgnores([
'**/node_modules',
'**/dist'
'**/dist',
// Vendored code
'src/pretix/static/leaflet',
'src/pretix/static/clipboard',
'src/pretix/static/cropper',
'src/pretix/static/lightbox',
'src/pretix/static/are-you-sure',
'src/pretix/static/vuejs',
'src/pretix/static/fontawesome',
'src/pretix/static/typeahead',
'src/pretix/static/moment',
'src/pretix/static/pdfjs',
'src/pretix/static/sortable',
'src/pretix/static/iframeresizer',
'src/pretix/static/bootstrap',
'src/pretix/static/d3',
'src/pretix/static/jsi18n',
'src/pretix/static/fabric',
'src/pretix/static/datetimepicker',
'src/pretix/static/charts',
'src/pretix/static/fileupload',
'src/pretix/static/seating',
'src/pretix/static/rest_framework',
'src/pretix/static/select2',
'src/pretix/static/schema',
'src/pretix/static/slider',
'src/pretix/static/jquery',
'src/pretix/static/colorpicker',
'src/pretix/static/rrule',
'src/pretix/static/pretixcontrol/js/jquery.qrcode.min.js',
'src/pretix/static/pretixpresale/js/widget/docready.js',
// Pre-vue JS code
'src/pretix/static/pretixbase/js/addressform.js',
'src/pretix/static/pretixbase/js/asynctask.js',
'src/pretix/static/pretixbase/js/details.js',
'src/pretix/static/pretixbase/js/gettextstub.js',
'src/pretix/static/pretixbase/js/i18nstring.js',
'src/pretix/static/pretixcontrol/js/menu.js',
'src/pretix/static/pretixcontrol/js/ui/editor.js',
'src/pretix/static/pretixcontrol/js/ui/geo.js',
'src/pretix/static/pretixcontrol/js/ui/main.js',
'src/pretix/static/pretixcontrol/js/ui/plugins.js',
'src/pretix/static/pretixcontrol/js/ui/subevent.js',
'src/pretix/static/pretixcontrol/js/ui/variations.js',
'src/pretix/static/pretixcontrol/js/ui/webauthn.js',
'src/pretix/static/pretixpresale/js/ui/cart.js',
'src/pretix/static/pretixpresale/js/ui/main.js',
'src/pretix/static/pretixpresale/js/ui/questions.js',
'src/pretix/static/pretixpresale/js/widget/floatformat.js',
'src/pretix/static/pretixpresale/js/widget/widget.js',
'src/pretix/plugins/banktransfer/static',
'src/pretix/plugins/paypal2/static',
'src/pretix/plugins/statistics/static',
'src/pretix/plugins/stripe/static',
// Plugin checkouts
'local',
// docs
'doc',
])
export default defineConfig([
+14 -4
View File
@@ -9,7 +9,8 @@
"version": "1.0.0",
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"vue": "^3.5.30"
"vue": "^3.5.30",
"vue-slicksort": "^2.0.5"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
@@ -4148,9 +4149,9 @@
}
},
"node_modules/smol-toml": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
"integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==",
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.1.tgz",
"integrity": "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
@@ -4635,6 +4636,15 @@
"vue-eslint-parser": "^10.0.0"
}
},
"node_modules/vue-slicksort": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/vue-slicksort/-/vue-slicksort-2.0.5.tgz",
"integrity": "sha512-fXz1YrNjhUbJK7o0tMk27mIr4pMAZYLSYvtmLazCtfpvz+zafPCn34ILDL8B7hT7WLVZKreYs6JVe5VWymqmzA==",
"license": "MIT",
"peerDependencies": {
"vue": ">=3.0.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+2 -1
View File
@@ -27,7 +27,8 @@
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"vue": "^3.5.30"
"vue": "^3.5.30",
"vue-slicksort": "^2.0.5"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
+6 -6
View File
@@ -37,7 +37,7 @@ dependencies = [
"css-inline==0.21.*",
"defusedcsv>=3.0.0",
"dnspython==2.*",
"Django[argon2]==5.2.*",
"Django[argon2]==5.2.*,>=5.2.17",
"django-bootstrap3==26.2",
"django-compressor==4.6.0",
"django-countries==9.0.*",
@@ -50,13 +50,13 @@ dependencies = [
"django-libsass==0.9",
"django-localflavor==5.1",
"django-markup",
"django-oauth-toolkit==3.4.*",
"django-oauth-toolkit==2.3.*",
"django-otp==1.7.*",
"django-phonenumber-field==8.5.*",
"django-querytagger==0.0.3",
"django-redis==7.0.*",
"django-scopes==2.1.*",
"django-statici18n==2.7.*",
"django-statici18n==2.8.*",
"djangorestframework==3.17.*",
"dnspython==2.8.*",
"drf_ujson2==1.7.*",
@@ -79,12 +79,12 @@ dependencies = [
"phonenumberslite==9.0.*",
"Pillow==12.3.*",
"pretix-plugin-build",
"protobuf==7.35.*",
"protobuf==7.36.*",
"psycopg2-binary",
"pycountry",
"pycparser==3.0",
"pycryptodome==3.23.*",
"pypdf==6.5.*",
"pypdf==6.11.*",
"python-bidi==0.6.*", # Support for Arabic in reportlab
"python-dateutil==2.9.*",
"pytz",
@@ -94,7 +94,7 @@ dependencies = [
"redis==7.4.*",
"reportlab==5.0.*",
"requests==2.34.*",
"sentry-sdk==2.66.*",
"sentry-sdk==2.68.*",
"sepaxml==2.7.*",
"stripe==7.9.*",
"text-unidecode==1.*",
+3 -3
View File
@@ -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'),
)
+1
View File
@@ -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)
+7 -3
View File
@@ -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
@@ -975,7 +979,7 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
'reusable_media_type_nfc_mf0aes',
'reusable_media_type_nfc_mf0aes_random_uid',
'reusable_media_usage_enforced',
'system_question_order',
'system_question_order', # TODO(questionnaires) - remove or replace
'tax_rule_payment',
'tax_rule_cancellation',
]
+158 -2
View File
@@ -53,6 +53,7 @@ from pretix.base.models import (
ItemVariation, ItemVariationMetaValue, Question, QuestionOption, Quota,
SalesChannel,
)
from pretix.base.models.items import Questionnaire, QuestionnaireChild
class InlineItemVariationSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
@@ -542,6 +543,7 @@ class LegacyDependencyValueField(serializers.CharField):
class QuestionSerializer(I18nAwareModelSerializer):
options = InlineQuestionOptionSerializer(many=True, required=False)
identifier = serializers.CharField(allow_null=True)
internal_name = serializers.CharField(allow_null=True, source='question', read_only=True)
dependency_value = LegacyDependencyValueField(source='dependency_values', required=False, allow_null=True)
class Meta:
@@ -550,7 +552,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', 'internal_name',)
def validate_identifier(self, value):
Question._clean_identifier(self.context['event'], value, self.instance)
@@ -619,13 +621,167 @@ class QuestionSerializer(I18nAwareModelSerializer):
options_data = validated_data.pop('options') if 'options' in validated_data else []
items = validated_data.pop('items', [])
question = Question.objects.create(**validated_data)
question = Question.objects.create(**validated_data, container_type=Question.ContainerType.ORDERPOSITION)
question.items.set(items)
for opt_data in options_data:
QuestionOption.objects.create(question=question, **opt_data)
return question
class QuestionRefField(serializers.PrimaryKeyRelatedField):
def to_representation(self, qc):
if not qc:
return None
elif qc.system_datafield:
return qc.system_datafield
elif qc.user_datafield_id:
return qc.user_datafield_id
else:
return None
def to_internal_value(self, data):
if type(data) == int:
return {'user_datafield': super().to_internal_value(data), 'system_datafield': None}
elif type(data) == str or data is None:
return {'user_datafield': None, 'system_datafield': data}
else:
self.fail('incorrect_type', data_type=type(data).__name__)
def use_pk_only_optimization(self):
return self.source == '*'
class InlineQuestionnaireChildSerializer(I18nAwareModelSerializer):
question = QuestionRefField(source='*', queryset=Question.objects.none())
dependency_question = QuestionRefField(allow_null=True, required=False, queryset=Question.objects.none())
class Meta:
model = QuestionnaireChild
fields = ('question', 'required', 'label', 'help_text', 'dependency_question', 'dependency_values')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["question"].queryset = self.context["event"].questions.all()
self.fields["dependency_question"].queryset = self.context["event"].questions.all()
def validate(self, data):
data = super().validate(data)
event = self.context['event']
full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
full_data.update(data)
if full_data.get('ask_during_checkin') and full_data.get('dependency_question'):
raise ValidationError('Dependencies are not supported during check-in.')
dep = full_data.get('dependency_question')
if dep:
if dep.ask_during_checkin:
raise ValidationError(_('Question cannot depend on a question asked during check-in.'))
seen_ids = {self.instance.pk} if self.instance else set()
while dep:
if dep.pk in seen_ids:
raise ValidationError(_('Circular dependency between questions detected.'))
seen_ids.add(dep.pk)
dep = dep.dependency_question
return data
def validate_dependency_question(self, value):
if value:
if value.type not in (Question.TYPE_CHOICE, Question.TYPE_BOOLEAN, Question.TYPE_CHOICE_MULTIPLE):
raise ValidationError('Question dependencies can only be set to boolean or choice questions.')
if value == self.instance:
raise ValidationError('A question cannot depend on itself.')
return value
class QuestionnaireSerializer(I18nAwareModelSerializer):
limit_sales_channels = serializers.SlugRelatedField(
slug_field="identifier",
queryset=SalesChannel.objects.none(),
required=False,
allow_empty=True,
many=True,
)
class Meta:
model = Questionnaire
fields = ('id', 'type', 'internal_name', 'items', 'position', 'all_sales_channels', 'limit_sales_channels', 'children')
def __init__(self, *args, **kwargs):
self.fields['children'] = InlineQuestionnaireChildSerializer(many=True, required=True, context=kwargs['context'], partial=False)
super().__init__(*args, **kwargs)
def validate(self, data):
data = super().validate(data)
event = self.context['event']
#full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
#full_data.update(data)
#if full_data.get('ask_during_checkin') and full_data.get('dependency_question'):
# raise ValidationError('Dependencies are not supported during check-in.')
#if full_data.get('ask_during_checkin') and full_data.get('type') in Question.ASK_DURING_CHECKIN_UNSUPPORTED:
# raise ValidationError(_('This type of question cannot be asked during check-in.'))
#if full_data.get('show_during_checkin') and full_data.get('type') in Question.SHOW_DURING_CHECKIN_UNSUPPORTED:
# raise ValidationError(_('This type of question cannot be shown during check-in.'))
#Question.clean_items(event, full_data.get('items') or [])
return data
def validate_children(self, value):
prev_questions = {}
for child in value:
if child.get('dependency_question'):
if (child['dependency_question']['user_datafield'] or child['dependency_question']['system_datafield']) not in prev_questions:
raise ValidationError('A question can only depend on a previous question from the same questionnaire.')
if child['user_datafield']:
prev_questions[child['user_datafield']] = child
if child['system_datafield']:
prev_questions[child['system_datafield']] = child
return value
@transaction.atomic
def create(self, validated_data):
children_data = validated_data.pop('children') if 'children' in validated_data else []
questionnaire = super().create(validated_data)
self.set_children(questionnaire, children_data)
return questionnaire
@transaction.atomic
def update(self, instance, validated_data):
children_data = validated_data.pop('children', None)
questionnaire = super().update(instance, validated_data)
if children_data is not None:
self.set_children(questionnaire, children_data)
return questionnaire
def set_children(self, questionnaire, new_data):
result = []
child_serializer = self.fields['children'].child
existing = questionnaire.children.all()
for i, d in enumerate(new_data):
d['questionnaire'] = questionnaire
d['position'] = i + 1
d.setdefault('required', False)
d.setdefault('help_text', None)
d.setdefault('dependency_question', None)
d.setdefault('dependency_values', None)
updatable = min(len(existing), len(new_data))
for i in range(0, updatable):
result.append(child_serializer.update(existing[i], new_data[i]))
for i in range(updatable, len(new_data)):
result.append(child_serializer.create(new_data[i]))
for i in range(updatable, len(existing)):
existing[i].delete()
return result
class QuotaSerializer(I18nAwareModelSerializer):
available = serializers.BooleanField(read_only=True)
available_number = serializers.IntegerField(read_only=True)
+2
View File
@@ -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.')
+2 -1
View File
@@ -79,7 +79,8 @@ event_router.register(r'subevents', event.SubEventViewSet)
event_router.register(r'clone', event.CloneEventViewSet)
event_router.register(r'items', item.ItemViewSet)
event_router.register(r'categories', item.ItemCategoryViewSet)
event_router.register(r'questions', item.QuestionViewSet)
event_router.register(r'datafields', item.QuestionViewSet)
event_router.register(r'questionnaires', item.QuestionnaireViewSet)
event_router.register(r'discounts', discount.DiscountViewSet)
event_router.register(r'quotas', item.QuotaViewSet)
event_router.register(r'vouchers', voucher.VoucherViewSet)
+8
View File
@@ -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(
@@ -837,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(
@@ -1064,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'),
)
+4
View File
@@ -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 '
+11
View File
@@ -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,
+7
View File
@@ -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',
+80 -2
View File
@@ -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
@@ -47,13 +48,15 @@ from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.item import (
ItemAddOnSerializer, ItemBundleSerializer, ItemCategorySerializer,
ItemProgramTimeSerializer, ItemSerializer, ItemVariationSerializer,
QuestionOptionSerializer, QuestionSerializer, QuotaSerializer,
QuestionnaireSerializer, QuestionOptionSerializer, QuestionSerializer,
QuotaSerializer,
)
from pretix.api.views import ConditionalListView
from pretix.base.models import (
CartPosition, Item, ItemAddOn, ItemBundle, ItemCategory, ItemProgramTime,
ItemVariation, Question, QuestionOption, Quota,
)
from pretix.base.models.items import Questionnaire
from pretix.base.services.quotas import QuotaAvailability
from pretix.helpers.dicts import merge_dicts
from pretix.helpers.i18n import i18ncomp
@@ -109,6 +112,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 +127,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 +144,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 +189,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 +204,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 +215,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 +258,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 +269,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 +279,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 +315,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 +326,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 +336,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 +369,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 +381,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 +391,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 +421,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 +436,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 +446,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
@@ -456,8 +477,12 @@ class QuestionViewSet(ConditionalListView, viewsets.ModelViewSet):
write_permission = 'event.items:write'
def get_queryset(self):
return self.request.event.questions.prefetch_related('options').all()
return self.request.event.questions.filter(
# the container_type parameter is undocumented, this API is going to change in a later release
container_type=self.request.GET.get('container_type', Question.ContainerType.ORDERPOSITION),
).prefetch_related('options').all()
@transaction.atomic()
def perform_create(self, serializer):
serializer.save(event=self.request.event)
serializer.instance.log_action(
@@ -472,6 +497,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 +507,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 +536,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 +547,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 +557,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',
@@ -538,6 +568,51 @@ class QuestionOptionViewSet(viewsets.ModelViewSet):
super().perform_destroy(instance)
class QuestionnaireViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = QuestionnaireSerializer
queryset = Questionnaire.objects.none()
#filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
#filterset_class = QuestionFilter
ordering_fields = ('id', 'position')
ordering = ('position', 'id')
permission = None
write_permission = 'event.items:write'
def get_queryset(self):
return self.request.event.questionnaires.prefetch_related('children').all()
def perform_create(self, serializer):
serializer.save(event=self.request.event)
serializer.instance.log_action(
'pretix.event.questionnaire.added',
user=self.request.user,
auth=self.request.auth,
data=self.request.data
)
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['event'] = self.request.event
return ctx
def perform_update(self, serializer):
serializer.save(event=self.request.event)
serializer.instance.log_action(
'pretix.event.questionnaire.changed',
user=self.request.user,
auth=self.request.auth,
data=self.request.data
)
def perform_destroy(self, instance):
instance.log_action(
'pretix.event.questionnaire.deleted',
user=self.request.user,
auth=self.request.auth,
)
super().perform_destroy(instance)
class NumberInFilter(django_filters.BaseInFilter, django_filters.NumberFilter):
pass
@@ -586,6 +661,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 +684,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 +740,7 @@ class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet):
)
serializer.instance.rebuild_cache()
@transaction.atomic()
def perform_destroy(self, instance):
instance.log_action(
'pretix.event.quota.deleted',
+3
View File
@@ -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.")
+4
View File
@@ -20,6 +20,7 @@
# <https://www.gnu.org/licenses/>.
#
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.')
+4
View File
@@ -20,6 +20,7 @@
# <https://www.gnu.org/licenses/>.
#
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',
+490 -428
View File
@@ -349,16 +349,22 @@ class WrappedPhonePrefixSelect(Select):
return super().render(name, value or self.initial, *args, **kwargs)
def get_context(self, name, value, attrs):
if value and self.choices[1][0] != value:
matching_choices = len([1 for p, c in self.choices if p == value])
# self.choices is lazy evaluated, needs to be realized to be modifiable
choices = list(self.choices)
if value and choices[1][0] != value:
matching_choices = len([1 for p, c in choices if p == value])
if matching_choices > 1:
# Some countries share a phone prefix, for example +1 is used all over the Americas.
# This causes a UX problem: If the default value or the existing data is +12125552368,
# the widget will just show the first <option> entry with value="+1" as selected,
# which alphabetically is America Samoa, although most numbers statistically are from
# the US. As a workaround, we detect this case and add an aditional choice value with
# the US. As a workaround, we detect this case and add an additional choice value with
# just <option value="+1">+1</option> without an explicit country.
self.choices.insert(1, (value, value))
self.choices = [
choices[0],
(value, value),
*choices[1:],
]
context = super().get_context(name, value, attrs)
return context
@@ -638,447 +644,243 @@ class PortraitImageField(SizeValidationMixin, ExtValidationMixin, forms.FileFiel
class BaseQuestionsForm(forms.Form):
"""
This form class is responsible for asking order-related questions. This includes
the attendee name for admission tickets, if the corresponding setting is enabled,
as well as additional questions defined by the organizer.
This is the base form class responsible for asking order- or ticket-related questions.
"""
address_validation = False
def __init__(self, *args, **kwargs):
"""
Takes two additional keyword arguments:
:param cartpos: The cart position the form should be for
:param event: The event this belongs to
"""
request = kwargs.pop('request', None)
cartpos = self.cartpos = kwargs.pop('cartpos', None)
orderpos = self.orderpos = kwargs.pop('orderpos', None)
pos = cartpos or orderpos
item = pos.item
questions = pos.item.questions_to_ask
event = kwargs.pop('event')
self.all_optional = kwargs.pop('all_optional', False)
self.attendee_addresses_required = event.settings.attendee_addresses_required and not self.all_optional
super().__init__(*args, **kwargs)
if cartpos and item.validity_mode == Item.VALIDITY_MODE_DYNAMIC and item.validity_dynamic_start_choice:
if item.validity_dynamic_start_choice_day_limit:
max_date = time_machine_now().astimezone(event.timezone) + timedelta(days=item.validity_dynamic_start_choice_day_limit)
else:
max_date = None
min_date = time_machine_now()
def build_user_question_field(self, request, event, answerlist, qc, datafield):
# Do we already have an answer? Provide it as the initial value
answers = [a for a in answerlist if a.question_id == datafield.id]
if answers:
initial = answers[0]
else:
initial = None
if (item.require_membership or (pos.variation and pos.variation.require_membership)) and pos.used_membership:
if pos.used_membership.date_start >= time_machine_now():
initial = min_date = pos.used_membership.date_start
max_date = min(max_date, pos.used_membership.date_end) if max_date else pos.used_membership.date_end
if item.validity_dynamic_duration_months or item.validity_dynamic_duration_days:
attrs = {}
if max_date:
attrs['data-max'] = max_date.date().isoformat()
if min_date:
attrs['data-min'] = min_date.date().isoformat()
self.fields['requested_valid_from'] = forms.DateField(
label=_('Start date'),
help_text='' if initial else _('If you keep this empty, the ticket will be valid starting at the time of purchase.'),
required=bool(initial),
initial=pos.requested_valid_from or initial,
widget=DatePickerWidget(attrs),
validators=([MaxDateValidator(max_date.date())] if max_date else []) + [MinDateValidator(min_date.date())]
)
tz = ZoneInfo(event.settings.timezone)
required = qc.required and not self.all_optional
if datafield.type == Question.TYPE_BOOLEAN:
if required:
# For some reason, django-bootstrap3 does not set the required attribute
# itself.
widget = forms.CheckboxInput(attrs={'required': 'required'})
else:
self.fields['requested_valid_from'] = forms.SplitDateTimeField(
label=_('Start date'),
help_text='' if initial else _('If you keep this empty, the ticket will be valid starting at the time of purchase.'),
required=bool(initial),
initial=pos.requested_valid_from or initial,
widget=SplitDateTimePickerWidget(
time_format=get_format_without_seconds('TIME_INPUT_FORMATS'),
min_date=min_date,
max_date=max_date
),
validators=([MaxDateTimeValidator(max_date)] if max_date else []) + [MinDateTimeValidator(min_date)]
)
widget = forms.CheckboxInput()
add_fields = {}
if initial:
initialbool = (initial.answer == "True")
else:
initialbool = False
if item.ask_attendee_data and event.settings.attendee_names_asked:
add_fields['attendee_name_parts'] = NamePartsFormField(
max_length=255,
required=event.settings.attendee_names_required and not self.all_optional,
scheme=event.settings.name_scheme,
titles=event.settings.name_scheme_titles,
label=_('Attendee name'),
initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts),
field = forms.BooleanField(
label=escape(qc.label), required=required,
help_text=rich_text(qc.help_text),
initial=initialbool, widget=widget,
)
if item.ask_attendee_data and event.settings.attendee_emails_asked:
add_fields['attendee_email'] = forms.EmailField(
required=event.settings.attendee_emails_required and not self.all_optional,
label=_('Attendee email'),
initial=(cartpos.attendee_email if cartpos else orderpos.attendee_email),
widget=forms.EmailInput(
attrs={
'autocomplete': 'email'
}
)
elif datafield.type == Question.TYPE_NUMBER:
field = forms.DecimalField(
label=escape(qc.label), required=required,
min_value=datafield.valid_number_min or Decimal('0.00'),
max_value=datafield.valid_number_max,
help_text=rich_text(qc.help_text),
initial=initial.answer if initial else None,
)
if item.ask_attendee_data and event.settings.attendee_company_asked:
add_fields['company'] = forms.CharField(
required=event.settings.attendee_company_required and not self.all_optional,
label=_('Company'),
max_length=255,
initial=(cartpos.company if cartpos else orderpos.company),
elif datafield.type == Question.TYPE_STRING:
field = forms.CharField(
label=escape(qc.label), required=required,
min_length=datafield.valid_string_length_min,
max_length=datafield.valid_string_length_max,
help_text=rich_text(qc.help_text),
initial=initial.answer if initial else None,
)
if item.ask_attendee_data and event.settings.attendee_addresses_asked:
add_fields['street'] = forms.CharField(
required=self.attendee_addresses_required,
label=_('Address'),
widget=forms.Textarea(attrs={
'rows': 2,
'placeholder': _('Street and Number'),
'autocomplete': 'street-address'
}),
initial=(cartpos.street if cartpos else orderpos.street),
elif datafield.type == Question.TYPE_TEXT:
field = forms.CharField(
label=escape(qc.label), required=required,
min_length=datafield.valid_string_length_min,
max_length=datafield.valid_string_length_max,
help_text=rich_text(qc.help_text),
widget=forms.Textarea,
initial=initial.answer if initial else None,
)
add_fields['zipcode'] = forms.CharField(
required=False,
max_length=30,
label=_('ZIP code'),
initial=(cartpos.zipcode if cartpos else orderpos.zipcode),
widget=forms.TextInput(attrs={
'autocomplete': 'postal-code',
}),
)
add_fields['city'] = forms.CharField(
required=False,
label=_('City'),
max_length=255,
initial=(cartpos.city if cartpos else orderpos.city),
widget=forms.TextInput(attrs={
'autocomplete': 'address-level2',
}),
)
country = (cartpos.country if cartpos else orderpos.country) or guess_country_from_request(request, event)
add_fields['country'] = CountryField(
countries=CachedCountries
elif datafield.type == Question.TYPE_COUNTRYCODE:
field = CountryField(
countries=CachedCountries,
blank=True, null=True, blank_label=' ',
).formfield(
required=self.attendee_addresses_required,
label=_('Country'),
initial=country,
widget=forms.Select(attrs={
'autocomplete': 'country',
'data-trigger-address-info': 'on',
}),
label=escape(qc.label), required=required,
help_text=rich_text(qc.help_text),
widget=forms.Select,
empty_label=' ',
initial=initial.answer if initial else (guess_country_from_request(request, event) if required else None),
)
c = [('', '---')]
fprefix = str(self.prefix) + '-' if self.prefix is not None and self.prefix != '-' else ''
cc = None
state = None
if fprefix + 'country' in self.data:
cc = str(self.data[fprefix + 'country'])
elif country:
cc = str(country)
if cc and cc in COUNTRIES_WITH_STATE_IN_ADDRESS:
types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[cc]
statelist = [s for s in pycountry.subdivisions.get(country_code=cc) if s.type in types]
c += sorted([(s.code[3:], s.name) for s in statelist], key=lambda s: s[1])
state = (cartpos.state if cartpos else orderpos.state)
elif fprefix + 'state' in self.data:
self.data = self.data.copy()
del self.data[fprefix + 'state']
add_fields['state'] = forms.ChoiceField(
label=pgettext_lazy('address', 'State'),
required=False,
choices=c,
initial=state,
widget=forms.Select(attrs={
'autocomplete': 'address-level1',
}),
elif datafield.type == Question.TYPE_CHOICE:
field = forms.ModelChoiceField(
queryset=datafield.options,
label=escape(qc.label), required=required,
help_text=rich_text(qc.help_text),
widget=forms.Select,
to_field_name='identifier',
empty_label='',
initial=initial.options.first() if initial else None,
)
add_fields['state'].widget.is_required = True
field_positions = list(
[
(n, event.settings.system_question_order.get(n if n != 'state' else 'country', 0))
for n in add_fields.keys()
]
)
for q in questions:
# Do we already have an answer? Provide it as the initial value
answers = [a for a in pos.answerlist if a.question_id == q.id]
if answers:
initial = answers[0]
elif datafield.type == Question.TYPE_CHOICE_MULTIPLE:
field = forms.ModelMultipleChoiceField(
queryset=datafield.options,
label=escape(qc.label), required=required,
help_text=rich_text(qc.help_text),
to_field_name='identifier',
widget=QuestionCheckboxSelectMultiple,
initial=initial.options.all() if initial else None,
)
elif datafield.type == Question.TYPE_FILE:
if datafield.valid_file_portrait:
field = PortraitImageField(
label=escape(qc.label), required=required,
help_text=rich_text(qc.help_text),
initial=initial.file if initial else None,
widget=PortraitImageWidget(answer=initial, request=request, attrs={'data-portrait-photo': 'true'}),
)
else:
initial = None
tz = ZoneInfo(event.settings.timezone)
help_text = rich_text(q.help_text)
label = escape(q.question) # django-bootstrap3 calls mark_safe
required = q.required and not self.all_optional
if q.type == Question.TYPE_BOOLEAN:
if required:
# For some reason, django-bootstrap3 does not set the required attribute
# itself.
widget = forms.CheckboxInput(attrs={'required': 'required'})
else:
widget = forms.CheckboxInput()
if initial:
initialbool = (initial.answer == "True")
else:
initialbool = False
field = forms.BooleanField(
label=label, required=required,
help_text=help_text,
initial=initialbool, widget=widget,
field = ExtFileField(
label=escape(qc.label), required=required,
help_text=rich_text(qc.help_text),
initial=initial.file if initial else None,
widget=UploadedFileWidget(answer=initial, request=request),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_OTHER,
max_size=settings.FILE_UPLOAD_MAX_SIZE_OTHER,
)
elif q.type == Question.TYPE_NUMBER:
field = forms.DecimalField(
label=label, required=required,
min_value=q.valid_number_min or Decimal('0.00'),
max_value=q.valid_number_max,
help_text=help_text,
initial=initial.answer if initial else None,
)
elif q.type == Question.TYPE_STRING:
field = forms.CharField(
label=label, required=required,
max_length=q.valid_string_length_max,
help_text=help_text,
initial=initial.answer if initial else None,
)
elif q.type == Question.TYPE_TEXT:
field = forms.CharField(
label=label, required=required,
max_length=q.valid_string_length_max,
help_text=help_text,
widget=forms.Textarea,
initial=initial.answer if initial else None,
)
elif q.type == Question.TYPE_COUNTRYCODE:
field = CountryField(
countries=CachedCountries,
blank=True, null=True, blank_label=' ',
).formfield(
label=label, required=required,
help_text=help_text,
widget=forms.Select,
empty_label=' ',
initial=initial.answer if initial else (guess_country_from_request(request, event) if required else None),
)
elif q.type == Question.TYPE_CHOICE:
field = forms.ModelChoiceField(
queryset=q.options,
label=label, required=required,
help_text=help_text,
widget=forms.Select,
to_field_name='identifier',
empty_label='',
initial=initial.options.first() if initial else None,
)
elif q.type == Question.TYPE_CHOICE_MULTIPLE:
field = forms.ModelMultipleChoiceField(
queryset=q.options,
label=label, required=required,
help_text=help_text,
to_field_name='identifier',
widget=QuestionCheckboxSelectMultiple,
initial=initial.options.all() if initial else None,
)
elif q.type == Question.TYPE_FILE:
if q.valid_file_portrait:
field = PortraitImageField(
label=label, required=required,
help_text=help_text,
initial=initial.file if initial else None,
widget=PortraitImageWidget(position=pos, event=event, answer=initial, attrs={'data-portrait-photo': 'true'}),
elif datafield.type == Question.TYPE_DATE:
attrs = {}
if datafield.valid_date_min:
attrs['data-min'] = datafield.valid_date_min.isoformat()
if datafield.valid_date_max:
attrs['data-max'] = datafield.valid_date_max.isoformat()
help_text = qc.help_text
if not help_text:
if datafield.valid_date_min and datafield.valid_date_max:
help_text = format_lazy(
_('Please enter a date between {min} and {max}.'),
min=date_format(datafield.valid_date_min, "SHORT_DATE_FORMAT"),
max=date_format(datafield.valid_date_max, "SHORT_DATE_FORMAT"),
)
else:
field = ExtFileField(
label=label, required=required,
help_text=help_text,
initial=initial.file if initial else None,
widget=UploadedFileWidget(position=pos, event=event, answer=initial),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_OTHER,
max_size=settings.FILE_UPLOAD_MAX_SIZE_OTHER,
elif datafield.valid_date_min:
help_text = format_lazy(
_('Please enter a date no earlier than {min}.'),
min=date_format(datafield.valid_date_min, "SHORT_DATE_FORMAT"),
)
elif q.type == Question.TYPE_DATE:
attrs = {}
if q.valid_date_min:
attrs['data-min'] = q.valid_date_min.isoformat()
if q.valid_date_max:
attrs['data-max'] = q.valid_date_max.isoformat()
if not help_text:
if q.valid_date_min and q.valid_date_max:
help_text = format_lazy(
_('Please enter a date between {min} and {max}.'),
min=date_format(q.valid_date_min, "SHORT_DATE_FORMAT"),
max=date_format(q.valid_date_max, "SHORT_DATE_FORMAT"),
)
elif q.valid_date_min:
help_text = format_lazy(
_('Please enter a date no earlier than {min}.'),
min=date_format(q.valid_date_min, "SHORT_DATE_FORMAT"),
)
elif q.valid_date_max:
help_text = format_lazy(
_('Please enter a date no later than {max}.'),
max=date_format(q.valid_date_max, "SHORT_DATE_FORMAT"),
)
if initial and initial.answer:
try:
_initial = dateutil.parser.parse(initial.answer).date()
except dateutil.parser.ParserError:
_initial = None
else:
elif datafield.valid_date_max:
help_text = format_lazy(
_('Please enter a date no later than {max}.'),
max=date_format(datafield.valid_date_max, "SHORT_DATE_FORMAT"),
)
if initial and initial.answer:
try:
_initial = dateutil.parser.parse(initial.answer).date()
except dateutil.parser.ParserError:
_initial = None
field = forms.DateField(
label=label, required=required,
help_text=help_text,
initial=_initial,
widget=DatePickerWidget(attrs),
)
if q.valid_date_min:
field.validators.append(MinDateValidator(q.valid_date_min))
if q.valid_date_max:
field.validators.append(MaxDateValidator(q.valid_date_max))
elif q.type == Question.TYPE_TIME:
if initial and initial.answer:
try:
_initial = dateutil.parser.parse(initial.answer).time()
except dateutil.parser.ParserError:
_initial = None
else:
else:
_initial = None
field = forms.DateField(
label=escape(qc.label), required=required,
help_text=rich_text(help_text),
initial=_initial,
widget=DatePickerWidget(attrs),
)
if datafield.valid_date_min:
field.validators.append(MinDateValidator(datafield.valid_date_min))
if datafield.valid_date_max:
field.validators.append(MaxDateValidator(datafield.valid_date_max))
elif datafield.type == Question.TYPE_TIME:
if initial and initial.answer:
try:
_initial = dateutil.parser.parse(initial.answer).time()
except dateutil.parser.ParserError:
_initial = None
field = forms.TimeField(
label=label, required=required,
help_text=help_text,
initial=_initial,
widget=TimePickerWidget(without_seconds=True),
)
elif q.type == Question.TYPE_DATETIME:
if not help_text:
if q.valid_datetime_min and q.valid_datetime_max:
help_text = format_lazy(
_('Please enter a date and time between {min} and {max}.'),
min=date_format(q.valid_datetime_min, "SHORT_DATETIME_FORMAT"),
max=date_format(q.valid_datetime_max, "SHORT_DATETIME_FORMAT"),
)
elif q.valid_datetime_min:
help_text = format_lazy(
_('Please enter a date and time no earlier than {min}.'),
min=date_format(q.valid_datetime_min, "SHORT_DATETIME_FORMAT"),
)
elif q.valid_datetime_max:
help_text = format_lazy(
_('Please enter a date and time no later than {max}.'),
max=date_format(q.valid_datetime_max, "SHORT_DATETIME_FORMAT"),
)
else:
_initial = None
field = forms.TimeField(
label=escape(qc.label), required=required,
help_text=rich_text(qc.help_text),
initial=_initial,
widget=TimePickerWidget(without_seconds=True),
)
elif datafield.type == Question.TYPE_DATETIME:
help_text = qc.help_text
if not help_text:
if datafield.valid_datetime_min and datafield.valid_datetime_max:
help_text = format_lazy(
_('Please enter a date and time between {min} and {max}.'),
min=date_format(datafield.valid_datetime_min, "SHORT_DATETIME_FORMAT"),
max=date_format(datafield.valid_datetime_max, "SHORT_DATETIME_FORMAT"),
)
elif datafield.valid_datetime_min:
help_text = format_lazy(
_('Please enter a date and time no earlier than {min}.'),
min=date_format(datafield.valid_datetime_min, "SHORT_DATETIME_FORMAT"),
)
elif datafield.valid_datetime_max:
help_text = format_lazy(
_('Please enter a date and time no later than {max}.'),
max=date_format(datafield.valid_datetime_max, "SHORT_DATETIME_FORMAT"),
)
if initial and initial.answer:
try:
_initial = dateutil.parser.parse(initial.answer).astimezone(tz)
except dateutil.parser.ParserError:
_initial = None
else:
if initial and initial.answer:
try:
_initial = dateutil.parser.parse(initial.answer).astimezone(tz)
except dateutil.parser.ParserError:
_initial = None
else:
_initial = None
field = SplitDateTimeField(
label=label, required=required,
help_text=help_text,
initial=_initial,
widget=SplitDateTimePickerWidget(
time_format=get_format_without_seconds('TIME_INPUT_FORMATS'),
min_date=q.valid_datetime_min,
max_date=q.valid_datetime_max
),
)
if q.valid_datetime_min:
field.validators.append(MinDateTimeValidator(q.valid_datetime_min))
if q.valid_datetime_max:
field.validators.append(MaxDateTimeValidator(q.valid_datetime_max))
elif q.type == Question.TYPE_PHONENUMBER:
if initial:
try:
initial = PhoneNumber().from_string(initial.answer)
except NumberParseException:
initial = None
field = SplitDateTimeField(
label=escape(qc.label), required=required,
help_text=rich_text(help_text),
initial=_initial,
widget=SplitDateTimePickerWidget(
time_format=get_format_without_seconds('TIME_INPUT_FORMATS'),
min_date=datafield.valid_datetime_min,
max_date=datafield.valid_datetime_max
),
)
if datafield.valid_datetime_min:
field.validators.append(MinDateTimeValidator(datafield.valid_datetime_min))
if datafield.valid_datetime_max:
field.validators.append(MaxDateTimeValidator(datafield.valid_datetime_max))
elif datafield.type == Question.TYPE_PHONENUMBER:
if initial:
try:
initial = PhoneNumber().from_string(initial.answer)
except NumberParseException:
initial = None
if not initial:
phone_prefix = guess_phone_prefix_from_request(request, event)
if phone_prefix:
initial = "+{}.".format(phone_prefix)
if not initial:
phone_prefix = guess_phone_prefix_from_request(request, event)
if phone_prefix:
initial = "+{}.".format(phone_prefix)
field = PhoneNumberField(
label=label, required=required,
help_text=help_text,
# We now exploit an implementation detail in PhoneNumberPrefixWidget to allow us to pass just
# a country code but no number as an initial value. It's a bit hacky, but should be stable for
# the future.
initial=initial,
widget=WrappedPhoneNumberPrefixWidget()
)
field.question = q
if answers:
# Cache the answer object for later use
field.answer = answers[0]
field = PhoneNumberField(
label=escape(qc.label), required=required,
help_text=rich_text(qc.help_text),
# We now exploit an implementation detail in PhoneNumberPrefixWidget to allow us to pass just
# a country code but no number as an initial value. It's a bit hacky, but should be stable for
# the future.
initial=initial,
widget=WrappedPhoneNumberPrefixWidget()
)
field.datafield = datafield
if answers:
# Cache the answer object for later use
field.answer = answers[0]
if q.dependency_question_id:
field.widget.attrs['data-question-dependency'] = q.dependency_question_id
field.widget.attrs['data-question-dependency-values'] = escapejson_attr(json.dumps(q.dependency_values))
if q.type != 'M':
field.widget.attrs['required'] = q.required and not self.all_optional
field._required = q.required and not self.all_optional
field.required = False
if qc.dependency_question_id:
field.widget.attrs['data-question-dependency'] = qc.dependency_question_id
field.widget.attrs['data-question-dependency-values'] = escapejson_attr(json.dumps(qc.dependency_values))
if datafield.type != 'M':
field.widget.attrs['required'] = qc.required and not self.all_optional
field._required = qc.required and not self.all_optional
field.required = False
add_fields['question_%s' % q.id] = field
field_positions.append(('question_%s' % q.id, q.position))
field_positions.sort(key=lambda e: e[1])
for fname, p in field_positions:
self.fields[fname] = add_fields[fname]
responses = question_form_fields.send(sender=event, position=pos)
data = pos.meta_info_data
for r, response in sorted(responses, key=lambda r: str(r[0])):
for key, value in response.items():
# We need to be this explicit, since OrderedDict.update does not retain ordering
self.fields[key] = value
value.initial = data.get('question_form_data', {}).get(key)
for k, v in self.fields.items():
if isinstance(v.widget, forms.MultiWidget):
for w in v.widget.widgets:
autocomplete = w.attrs.get('autocomplete', '')
if autocomplete.strip() == "off":
w.attrs['autocomplete'] = 'off'
else:
w.attrs['autocomplete'] = 'section-{} '.format(self.prefix) + autocomplete
if v.widget.attrs.get('autocomplete') or k == 'attendee_name_parts':
autocomplete = v.widget.attrs.get('autocomplete', '')
if autocomplete.strip() == "off":
v.widget.attrs['autocomplete'] = 'off'
else:
v.widget.attrs['autocomplete'] = 'section-{} '.format(self.prefix) + autocomplete
def clean(self):
from pretix.base.addressvalidation import \
validate_address # local import to prevent impact on startup time
d = super().clean()
if self.address_validation:
self.cleaned_data = d = validate_address(d, all_optional=not self.attendee_addresses_required)
if d.get('street') and d.get('country') and str(d['country']) in COUNTRIES_WITH_STATE_IN_ADDRESS:
if not d.get('state'):
self.add_error('state', _('This field is required.'))
return field
def check_user_questions(self, d):
question_cache = {f.question.pk: f.question for f in self.fields.values() if getattr(f, 'question', None)}
def question_is_visible(parentid, qvals):
@@ -1123,6 +925,265 @@ class BaseQuestionsForm(forms.Form):
if 'question_%d' % q.pk in d and d['question_%d' % q.pk] is False:
d['question_%d' % q.pk] = None
class OrderLevelQuestionsForm(BaseQuestionsForm):
def __init__(self, container, *args, **kwargs):
"""
Takes two additional keyword arguments:
:param checkoutsession: The checkout session the form should be for
:param order: The order the form should be for
:param event: The event this belongs to
"""
request = kwargs.pop('request', None)
event = kwargs.pop('event')
self.all_optional = kwargs.pop('all_optional', False)
super().__init__(*args, **kwargs)
# TODO(questionnaires) - switch olq's to questionnaires !
questions = Question.objects.filter(
event=event, container_type=Question.ContainerType.ORDER,
ask_during_checkin=False, hidden=False,
).order_by('position')
answerlist = container.answers.prefetch_related('options')
for q in questions:
self.fields['question_%s' % q.id] = self.build_user_question_field(request, event, answerlist, q)
def clean(self):
d = super().clean()
self.check_user_questions(d)
return d
class TicketLevelQuestionsForm(BaseQuestionsForm):
"""
This form class is responsible for asking ticket-related questions. This includes
the attendee name for admission tickets, if the corresponding setting is enabled,
as well as additional questions defined by the organizer.
"""
def __init__(self, *args, **kwargs):
"""
Takes two additional keyword arguments:
:param cartpos: The cart position the form should be for
:param event: The event this belongs to
"""
request = kwargs.pop('request', None)
cartpos = self.cartpos = kwargs.pop('cartpos', None)
orderpos = self.orderpos = kwargs.pop('orderpos', None)
pos = cartpos or orderpos
item = pos.item
questionnaires = pos.item.relevant_questionnaires
event = kwargs.pop('event')
self.all_optional = kwargs.pop('all_optional', False)
self.attendee_addresses_required = event.settings.attendee_addresses_required and not self.all_optional
super().__init__(*args, **kwargs)
if cartpos and item.validity_mode == Item.VALIDITY_MODE_DYNAMIC and item.validity_dynamic_start_choice:
self.fields['requested_valid_from'] = self.build_requested_valid_from_field(event, pos, item)
for questionnaire in questionnaires:
for child in getattr(questionnaire, 'childlist', questionnaire.children.all()):
if child.user_datafield:
df = child.user_datafield
self.fields['question_%s' % df.id] = self.build_user_question_field(request, event, pos.answerlist, child, df)
elif child.system_datafield:
self.fields[child.system_datafield] = self.build_system_question_field(request, event, pos, child)
responses = question_form_fields.send(sender=event, position=pos)
data = pos.meta_info_data
for r, response in sorted(responses, key=lambda r: str(r[0])):
for key, value in response.items():
# We need to be this explicit, since OrderedDict.update does not retain ordering
self.fields[key] = value
value.initial = data.get('question_form_data', {}).get(key)
for k, v in self.fields.items():
if isinstance(v.widget, forms.MultiWidget):
for w in v.widget.widgets:
autocomplete = w.attrs.get('autocomplete', '')
if autocomplete.strip() == "off":
w.attrs['autocomplete'] = 'off'
else:
w.attrs['autocomplete'] = 'section-{} '.format(self.prefix) + autocomplete
if v.widget.attrs.get('autocomplete') or k == 'attendee_name_parts':
autocomplete = v.widget.attrs.get('autocomplete', '')
if autocomplete.strip() == "off":
v.widget.attrs['autocomplete'] = 'off'
else:
v.widget.attrs['autocomplete'] = 'section-{} '.format(self.prefix) + autocomplete
def build_requested_valid_from_field(self, event, pos, item):
if item.validity_dynamic_start_choice_day_limit:
max_date = time_machine_now().astimezone(event.timezone) + timedelta(days=item.validity_dynamic_start_choice_day_limit)
else:
max_date = None
min_date = time_machine_now()
initial = None
if (item.require_membership or (pos.variation and pos.variation.require_membership)) and pos.used_membership:
if pos.used_membership.date_start >= time_machine_now():
initial = min_date = pos.used_membership.date_start
max_date = min(max_date, pos.used_membership.date_end) if max_date else pos.used_membership.date_end
if item.validity_dynamic_duration_months or item.validity_dynamic_duration_days:
attrs = {}
if max_date:
attrs['data-max'] = max_date.date().isoformat()
if min_date:
attrs['data-min'] = min_date.date().isoformat()
return forms.DateField(
label=_('Start date'),
help_text='' if initial else _('If you keep this empty, the ticket will be valid starting at the time of purchase.'),
required=bool(initial),
initial=pos.requested_valid_from or initial,
widget=DatePickerWidget(attrs),
validators=([MaxDateValidator(max_date.date())] if max_date else []) + [MinDateValidator(min_date.date())]
)
else:
return forms.SplitDateTimeField(
label=_('Start date'),
help_text='' if initial else _('If you keep this empty, the ticket will be valid starting at the time of purchase.'),
required=bool(initial),
initial=pos.requested_valid_from or initial,
widget=SplitDateTimePickerWidget(
time_format=get_format_without_seconds('TIME_INPUT_FORMATS'),
min_date=min_date,
max_date=max_date
),
validators=([MaxDateTimeValidator(max_date)] if max_date else []) + [MinDateTimeValidator(min_date)]
)
def build_system_question_field(self, request, event, pos, qc):
field_name = qc.system_datafield
if field_name == 'attendee_name_parts':
return NamePartsFormField(
max_length=255,
required=qc.required and not self.all_optional,
scheme=event.settings.name_scheme,
titles=event.settings.name_scheme_titles,
label=escape(qc.label),
help_text=rich_text(qc.help_text),
initial=pos.attendee_name_parts,
)
if field_name == 'attendee_email':
return forms.EmailField(
required=qc.required and not self.all_optional,
label=escape(qc.label),
help_text=rich_text(qc.help_text),
initial=pos.attendee_email,
widget=forms.EmailInput(
attrs={
'autocomplete': 'email'
}
)
)
if field_name == 'company':
return forms.CharField(
required=qc.required and not self.all_optional,
label=escape(qc.label),
help_text=rich_text(qc.help_text),
max_length=255,
initial=pos.company,
)
if field_name == 'street':
return forms.CharField(
required=qc.required and not self.all_optional,
label=escape(qc.label),
help_text=rich_text(qc.help_text),
widget=forms.Textarea(attrs={
'rows': 2,
'placeholder': _('Street and Number'),
'autocomplete': 'street-address'
}),
initial=pos.street,
)
if field_name == 'zipcode':
return forms.CharField(
required=False,
max_length=30,
label=escape(qc.label),
help_text=rich_text(qc.help_text),
initial=pos.zipcode,
widget=forms.TextInput(attrs={
'autocomplete': 'postal-code',
}),
)
if field_name == 'city':
return forms.CharField(
required=False,
label=escape(qc.label),
help_text=rich_text(qc.help_text),
max_length=255,
initial=pos.city,
widget=forms.TextInput(attrs={
'autocomplete': 'address-level2',
}),
)
if field_name == 'country':
country = pos.country or guess_country_from_request(request, event)
return CountryField(
countries=CachedCountries
).formfield(
required=qc.required and not self.all_optional,
label=escape(qc.label),
help_text=rich_text(qc.help_text),
initial=country,
widget=forms.Select(attrs={
'autocomplete': 'country',
'data-trigger-address-info': 'on',
}),
)
if field_name == 'state':
country = pos.country or guess_country_from_request(request, event)
c = [('', '---')]
fprefix = str(self.prefix) + '-' if self.prefix is not None and self.prefix != '-' else ''
cc = None
state = None
if fprefix + 'country' in self.data:
cc = str(self.data[fprefix + 'country'])
elif country:
cc = str(country)
if cc and cc in COUNTRIES_WITH_STATE_IN_ADDRESS:
types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[cc]
statelist = [s for s in pycountry.subdivisions.get(country_code=cc) if s.type in types]
c += sorted([(s.code[3:], s.name) for s in statelist], key=lambda s: s[1])
state = (pos.state)
elif fprefix + 'state' in self.data:
self.data = self.data.copy()
del self.data[fprefix + 'state']
field = forms.ChoiceField(
label=escape(qc.label),
help_text=rich_text(qc.help_text),
required=False,
choices=c,
initial=state,
widget=forms.Select(attrs={
'autocomplete': 'address-level1',
}),
)
field.widget.is_required = True
return field
def clean(self):
from pretix.base.addressvalidation import \
validate_address # local import to prevent impact on startup time
d = super().clean()
if self.address_validation:
self.cleaned_data = d = validate_address(d, all_optional=not self.attendee_addresses_required)
if d.get('street') and d.get('country') and str(d['country']) in COUNTRIES_WITH_STATE_IN_ADDRESS:
if not d.get('state'):
self.add_error('state', _('This field is required.'))
self.check_user_questions(d)
return d
@@ -1201,15 +1262,15 @@ class BaseInvoiceAddressForm(forms.ModelForm):
if not self.ask_vat_id:
del self.fields['vat_id']
elif self.validate_vat_id:
self.fields['vat_id'].help_text = '<br/>'.join([
str(_('Optional, but depending on the country you reside in we might need to charge you '
'additional taxes if you do not enter it.')),
])
self.fields['vat_id'].help_text = _(
'Optional, but depending on the country you reside in we might need to charge you '
'additional taxes if you do not enter it.'
)
else:
self.fields['vat_id'].help_text = '<br/>'.join([
str(_('Optional, but it might be required for you to claim tax benefits on your invoice '
'depending on your and the sellers country of residence.')),
])
self.fields['vat_id'].help_text = _(
'Optional, but it might be required for you to claim tax benefits on your invoice '
'depending on your and the sellers country of residence.'
)
transmission_type_choices = [
(t.identifier, t.public_name) for t in get_transmission_types()
@@ -1295,8 +1356,8 @@ class BaseInvoiceAddressForm(forms.ModelForm):
del self.fields['beneficiary']
if event.settings.invoice_address_custom_field:
self.fields['custom_field'].label = event.settings.invoice_address_custom_field
self.fields['custom_field'].help_text = event.settings.invoice_address_custom_field_helptext
self.fields['custom_field'].label = escape(event.settings.invoice_address_custom_field)
self.fields['custom_field'].help_text = rich_text(event.settings.invoice_address_custom_field_helptext)
else:
del self.fields['custom_field']
@@ -1410,7 +1471,8 @@ class BaseInvoiceAddressForm(forms.ModelForm):
pass # Skip re-validation if it is validated
elif self.validate_vat_id and vat_id_applicable:
try:
normalized_id = validate_vat_id(data.get('vat_id'), str(data.get('country')))
requester_id = self.request.event.settings.invoice_address_from_vat_id
normalized_id = validate_vat_id(data.get('vat_id'), str(data.get('country')), requester_id)
self.instance.vat_id_validated = bool(normalized_id)
self.instance.vat_id = data['vat_id'] = normalized_id
except VATIDFinalError as e:
+24 -18
View File
@@ -47,6 +47,7 @@ from pretix.helpers.i18n import (
get_format_without_seconds, get_javascript_format,
get_javascript_format_without_seconds,
)
from pretix.helpers.safedownload import get_token
def replace_arabic_numbers(inp):
@@ -157,36 +158,26 @@ class TimePickerWidget(forms.TimeInput):
class UploadedFileWidget(forms.ClearableFileInput):
def __init__(self, *args, **kwargs):
self.position = kwargs.pop('position')
self.event = kwargs.pop('event')
self.answer = kwargs.pop('answer')
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
class FakeFile:
def __init__(self, file, position, event, answer):
def __init__(self, file, answer, request):
self.file = file
self.position = position
self.event = event
self.answer = answer
self.request = request
def __str__(self):
return os.path.basename(self.file.name).split('.', 1)[-1]
@property
def url(self):
from pretix.base.models import OrderPosition
from pretix.multidomain.urlreverse import eventreverse
if isinstance(self.position, OrderPosition):
return eventreverse(self.event, 'presale:event.order.download.answer', kwargs={
'order': self.position.order.code,
'secret': self.position.order.secret,
'answer': self.answer.pk,
})
token = get_token(self.request, self.answer)
if self.request.resolver_match.namespace == 'control':
return self.answer.backend_file_url + '?token=' + token
else:
return eventreverse(self.event, 'presale:event.cart.download.answer', kwargs={
'answer': self.answer.pk,
})
return self.answer.frontend_file_url + '?token=' + token
def get_context(self, name, value, attrs):
# Browsers can't recognize that the server already has a file uploaded
@@ -199,7 +190,7 @@ class UploadedFileWidget(forms.ClearableFileInput):
def format_value(self, value):
if self.is_initial(value):
return self.FakeFile(value, self.position, self.event, self.answer)
return self.FakeFile(value, self.answer, self.request)
class SplitDateTimePickerWidget(forms.SplitDateTimeWidget):
@@ -314,3 +305,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
@@ -0,0 +1,63 @@
# Generated by Django 4.2.17 on 2025-01-01 20:25
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretixbase", "0307_devicelastseen"),
]
operations = [
migrations.CreateModel(
name="CheckoutSession",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
("cart_id", models.CharField(max_length=255, unique=True)),
("created", models.DateTimeField(auto_now_add=True)),
("testmode", models.BooleanField(default=False)),
("session_data", models.JSONField(default=dict)),
(
"customer",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="checkout_sessions",
to="pretixbase.customer",
),
),
(
"event",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="checkout_sessions",
to="pretixbase.event",
),
),
(
"sales_channel",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="pretixbase.saleschannel",
),
),
],
),
migrations.AddField(
model_name="invoiceaddress",
name="checkout_session",
field=models.OneToOneField(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="invoice_address",
to="pretixbase.checkoutsession",
),
),
]
@@ -0,0 +1,33 @@
# Generated by Django 5.2.15 on 2026-08-07 20:01
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0308_checkoutsession_invoiceaddress_checkout_session'),
]
operations = [
migrations.AddField(
model_name='question',
name='container_type',
field=models.CharField(default='P', max_length=5),
),
migrations.AddField(
model_name='questionanswer',
name='checkoutsession',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='pretixbase.checkoutsession'),
),
migrations.AddField(
model_name='questionanswer',
name='order',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='pretixbase.order'),
),
migrations.AlterUniqueTogether(
name='questionanswer',
unique_together={('cartposition', 'question'), ('checkoutsession', 'question'), ('order', 'question'), ('orderposition', 'question')},
),
]
@@ -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),
),
]
@@ -0,0 +1,196 @@
# Generated by Django 4.2.29 on 2026-03-19 14:24
import json
from collections import namedtuple
from itertools import chain, groupby
from django.db import migrations, models
import django.db.models.deletion
import i18nfield.fields
from i18nfield.strings import LazyI18nString
import pretix.base.models.base
import pretix.base.models.fields
FakeQuestion = namedtuple(
'FakeQuestion', 'id question position required'
)
def get_fake_questions(settings):
def b(s):
return s == 'True'
fq = []
sqo = json.loads(settings.get('system_question_order', '{}'))
_ = LazyI18nString.from_gettext
if b(settings.get('attendee_names_asked', 'True')):
fq.append(FakeQuestion('attendee_name_parts', _('Attendee name'), sqo.get('attendee_name_parts', 0), b(settings.get('attendee_names_required'))))
if b(settings.get('attendee_emails_asked')):
fq.append(FakeQuestion('attendee_email', _('Attendee email'), sqo.get('attendee_email', 0), b(settings.get('attendee_emails_required'))))
if b(settings.get('attendee_company_asked')):
fq.append(FakeQuestion('company', _('Company'), sqo.get('company', 0), b(settings.get('attendee_company_required'))))
if b(settings.get('attendee_addresses_asked')):
fq.append(FakeQuestion('street', _('Street'), sqo.get('street', 0), b(settings.get('attendee_addresses_required'))))
fq.append(FakeQuestion('zipcode', _('ZIP code'), sqo.get('zipcode', 0), b(settings.get('attendee_addresses_required'))))
fq.append(FakeQuestion('city', _('City'), sqo.get('city', 0), b(settings.get('attendee_addresses_required'))))
fq.append(FakeQuestion('country', _('Country'), sqo.get('country', 0), b(settings.get('attendee_addresses_required'))))
return fq
def migrate_questions_forward(apps, schema_editor):
Event = apps.get_model("pretixbase", "Event")
Item = apps.get_model("pretixbase", "Item")
Question = apps.get_model("pretixbase", "Question")
Questionnaire = apps.get_model("pretixbase", "Questionnaire")
QuestionnaireChild = apps.get_model("pretixbase", "QuestionnaireChild")
EventSettingsStore = apps.get_model('pretixbase', 'Event_SettingsStore')
def create_grouped_item_questionnaires(event, children, label_prefix, questionnaire_type):
# group by item, creating a unique questionnaire per item
item_questionnaires = (([t[3] for t in children], item_id) for item_id, children in
groupby(children, key=lambda t: t[0]))
# group again, merging all questionnaires with identical children
merged_questionnaires = groupby(sorted(item_questionnaires, key=lambda t: [q.id for q in t[0]]),
key=lambda t: t[0])
for children, iterator in merged_questionnaires:
items = [item for _c, item in iterator]
# create questionnaires and children
questionnaire = Questionnaire.objects.create(
event=event, type=questionnaire_type, position=0, all_sales_channels=True,
internal_name=label_prefix + ', '.join(str(iname or name) for (id, iname, name) in items)
)
questionnaire.items.set([id for (id, iname, name) in items])
deps = {}
for position, child in enumerate(children):
if isinstance(child, FakeQuestion):
QuestionnaireChild.objects.create(
questionnaire=questionnaire,
position=position + 1,
system_datafield=child.id,
required=child.required,
label=child.question,
)
else:
deps[child.id] = QuestionnaireChild.objects.create(
questionnaire=questionnaire,
position=position + 1,
user_datafield=child,
required=child.required,
label=child.question,
help_text=child.help_text,
dependency_question=deps[child.dependency_question.id] if child.dependency_question else None,
dependency_values=child.dependency_values,
)
for event in Event.objects.iterator():
# get relevant settings
settings = {
setting.key: setting.value for setting in EventSettingsStore.objects.filter(object_id=event.id, key__in=(
'system_question_order', 'attendee_names_asked', 'attendee_names_required', 'attendee_emails_asked', 'attendee_emails_required',
'attendee_company_asked', 'attendee_company_required', 'attendee_addresses_asked', 'attendee_addresses_required',
))
}
# get all ticket-level questions (user-defined and system provided), along with the products for which they're asked
questions = event.questions.filter(container_type='P')
children = sorted(chain((
(item, q.position, 0, q)
for q in get_fake_questions(settings)
for item in event.items.filter(personalized=True).values_list('id', 'internal_name', 'name')
), (
(item, q.position, q.id, q)
for q in questions.filter(hidden=False)
for item in q.items.values_list('id', 'internal_name', 'name')
)), key=lambda t: (t[0], t[1]))
create_grouped_item_questionnaires(event, children, '', 'PS')
children = sorted(chain((
(item, q.position, q.id, q)
for q in questions.filter(hidden=True)
for item in q.items.values_list('id', 'internal_name', 'name')
)), key=lambda t: (t[0], t[1]))
create_grouped_item_questionnaires(event, children, 'Hidden questions for ', 'PH')
# get all order-level questions
questions = list(event.questions.filter(container_type='O', hidden=False).order_by('position'))
if questions:
# create questionnaires and children
questionnaire = Questionnaire.objects.create(
event=event, type='OS', position=0, all_sales_channels=True,
internal_name='Per-order questions',
)
deps = {}
for position, child in enumerate(questions):
deps[child.id] = QuestionnaireChild.objects.create(
questionnaire=questionnaire,
position=position + 1,
user_datafield=child,
required=child.required,
label=child.question,
help_text=child.help_text,
dependency_question=deps[child.dependency_question.id] if child.dependency_question else None,
dependency_values=child.dependency_values,
)
def migrate_questions_backward(apps, schema_editor):
pass # as long as we don't delete the old columns, this is a no op. after that, it gets complicated...
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0309_alter_questionanswer_unique_together_and_more'),
]
operations = [
migrations.CreateModel(
name='Questionnaire',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('internal_name', models.CharField(max_length=255)),
('type', models.CharField(max_length=5)),
('position', models.PositiveIntegerField(default=0)),
('all_sales_channels', models.BooleanField(default=True)),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questionnaires', to='pretixbase.event')),
('items', models.ManyToManyField(related_name='questionnaires', to='pretixbase.item')),
('limit_sales_channels', models.ManyToManyField(to='pretixbase.saleschannel')),
],
options={
'abstract': False,
},
bases=(models.Model, pretix.base.models.base.LoggingMixin),
),
migrations.CreateModel(
name='QuestionnaireChild',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('position', models.PositiveIntegerField(default=0)),
('system_datafield', models.CharField(max_length=25, null=True)),
('required', models.BooleanField(default=False)),
('label', i18nfield.fields.I18nTextField()),
('help_text', i18nfield.fields.I18nTextField(null=True)),
('dependency_values', pretix.base.models.fields.MultiStringField(default=[])),
('dependency_question', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dependent_questions', to='pretixbase.questionnairechild')),
('questionnaire', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='children', to='pretixbase.questionnaire')),
('user_datafield', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='references', to='pretixbase.question')),
],
options={
'abstract': False,
},
bases=(models.Model, pretix.base.models.base.LoggingMixin),
),
migrations.RunPython(
migrate_questions_forward,
migrate_questions_backward,
),
# TODO(questionnaires) remove old columns from Question model
]
+2 -2
View File
@@ -37,8 +37,8 @@ from .invoices import Invoice, InvoiceLine, invoice_filename
from .items import (
Item, ItemAddOn, ItemBundle, ItemCategory, ItemMetaProperty, ItemMetaValue,
ItemProgramTime, ItemVariation, ItemVariationMetaValue, Question,
QuestionOption, Quota, SubEventItem, SubEventItemVariation,
itempicture_upload_to,
Questionnaire, QuestionnaireChild, QuestionOption, Quota, SubEventItem,
SubEventItemVariation, itempicture_upload_to,
)
from .log import LogEntry
from .mail import OutgoingMail
+12
View File
@@ -33,6 +33,7 @@ from django.db.models.aggregates import Sum
from django.db.models.expressions import OuterRef, Subquery
from django.db.models.functions.comparison import Coalesce
from django.utils.crypto import get_random_string, salted_hmac
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django_scopes import ScopedManager, scopes_disabled
@@ -409,6 +410,17 @@ class AttendeeProfile(models.Model):
return '\n'.join([str(p).strip() for p in parts if p and str(p).strip()])
@cached_property
def answers_key_to_index(self):
return {a.get('field_name'): i for i, a in enumerate(self.answers)}
def store_answer(self, answer_dict):
k = answer_dict['field_name']
if k in self.answers_key_to_index:
self.answers[self.answers_key_to_index[k]] = answer_dict
else:
self.answers.append(answer_dict)
def generate_client_id():
return get_random_string(40)
+7
View File
@@ -20,11 +20,13 @@
# <https://www.gnu.org/licenses/>.
#
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)
+1 -1
View File
@@ -736,7 +736,7 @@ class Event(EventMixin, LoggedModel):
self.settings.mail_send_order_paid_attendee = True
self.settings.mail_send_order_approved_attendee = True
self.settings.mail_send_order_approved_free_attendee = True
self.settings.mail_text_download_reminder_attendee = True
self.settings.mail_send_download_reminder_attendee = True
@property
def social_image(self):
+141 -17
View File
@@ -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
@@ -1569,10 +1569,12 @@ class ItemBundle(models.Model):
class Question(LoggedModel):
"""
A question is an input field that can be used to extend a ticket by custom information,
e.g. "Attendee age". The answers are found next to the position. The answers may be found
in QuestionAnswers, attached to OrderPositions/CartPositions. A question can allow one of
several input types, currently:
A question is a data field that can be used to extend an order or a ticket by custom
information, e.g. "Attendee age". To be actually useful, questions need to be added to
one or multiple Questionnaires. The answers may be found in QuestionAnswers, attached
to Orders, OrderPositions or CartPositions.
A question can allow one of several input types, currently:
* a number (``TYPE_NUMBER``)
* a one-line string (``TYPE_STRING``)
@@ -1592,7 +1594,7 @@ class Question(LoggedModel):
:param required: Whether answering this question is required for submitting an order including
items associated with this question.
:type required: bool
:param items: A set of ``Items`` objects that this question should be applied to
:param items: TO BE REMOVED
:param ask_during_checkin: Whether to ask this question during check-in instead of during check-out.
:type ask_during_checkin: bool
:param show_during_checkin: Whether to show the answer to this question during check-in.
@@ -1606,6 +1608,9 @@ class Question(LoggedModel):
:param dependency_values: The values that `dependency_question` needs to be set to for this question to be applicable.
:type dependency_values: list[str]
"""
class ContainerType(models.TextChoices):
ORDER = "O", _("Order")
ORDERPOSITION = "P", _("Order position")
TYPE_NUMBER = "N"
TYPE_STRING = "S"
TYPE_TEXT = "T"
@@ -1641,7 +1646,14 @@ class Question(LoggedModel):
related_name="questions",
on_delete=models.CASCADE
)
container_type = models.CharField(
max_length=5,
choices=ContainerType.choices,
verbose_name=_("Asked on"),
default=ContainerType.ORDERPOSITION,
)
question = I18nTextField(
# TODO(questionnaires) : to be renamed to 'internal_name'
verbose_name=_("Question")
)
identifier = models.CharField(
@@ -1657,6 +1669,7 @@ class Question(LoggedModel):
],
)
help_text = I18nTextField(
# TODO(questionnaires) : to be removed
verbose_name=_("Help text"),
help_text=_("If the question needs to be explained or clarified, do it here!"),
null=True, blank=True,
@@ -1666,22 +1679,22 @@ class Question(LoggedModel):
choices=TYPE_CHOICES,
verbose_name=_("Question type")
)
required = models.BooleanField(
required = models.BooleanField( # TODO(questionnaires) : to be removed, -> QuestionnaireChild
default=False,
verbose_name=_("Required question")
)
items = models.ManyToManyField(
items = models.ManyToManyField( # TODO(questionnaires) : to be removed, -> Questionnaire
Item,
related_name='questions',
verbose_name=_("Products"),
blank=True,
help_text=_('This question will be asked to buyers of the selected products')
)
position = models.PositiveIntegerField(
position = models.PositiveIntegerField( # TODO(questionnaires) : to be removed, -> Questionnaire + QuestionnaireChild
default=0,
verbose_name=_("Position")
)
ask_during_checkin = models.BooleanField(
ask_during_checkin = models.BooleanField( # TODO(questionnaires) : to be removed
verbose_name=_('Ask during check-in instead of in the ticket buying process'),
help_text=_('Not supported by all check-in apps for all question types.'),
default=False
@@ -1691,7 +1704,7 @@ class Question(LoggedModel):
help_text=_('Not supported by all check-in apps for all question types.'),
default=False
)
hidden = models.BooleanField(
hidden = models.BooleanField( # to be removed
verbose_name=_('Hidden question'),
help_text=_('This question will only show up in the backend.'),
default=False
@@ -1700,10 +1713,10 @@ class Question(LoggedModel):
verbose_name=_('Print answer on invoices'),
default=False
)
dependency_question = models.ForeignKey(
dependency_question = models.ForeignKey( # TODO(questionnaires) : to be removed, -> QuestionnaireChild
'Question', null=True, blank=True, on_delete=models.SET_NULL, related_name='dependent_questions'
)
dependency_values = MultiStringField(default=[])
dependency_values = MultiStringField(default=[]) # TODO(questionnaires) : to be removed, -> QuestionnaireChild
valid_number_min = models.DecimalField(decimal_places=6, max_digits=30, null=True, blank=True,
verbose_name=_('Minimum value'),
help_text=_('Currently not supported in our apps and during check-in'))
@@ -1722,6 +1735,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=_(
@@ -1737,9 +1755,9 @@ class Question(LoggedModel):
objects = ScopedManager(organizer='event__organizer')
class Meta:
verbose_name = _("Question")
verbose_name_plural = _("Questions")
ordering = ('position', 'id')
verbose_name = _("Data field")
verbose_name_plural = _("Data fields")
ordering = ('question', 'id')
unique_together = (('event', 'identifier'),)
def __str__(self):
@@ -1876,6 +1894,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,
@@ -1885,7 +1908,7 @@ class Question(LoggedModel):
return answer
@staticmethod
def clean_items(event, items):
def clean_items(event, items): # TODO(questionnaires) : remove method / move to qc
for item in items:
if event != item.event:
raise ValidationError(_('One or more items do not belong to this event.'))
@@ -1897,6 +1920,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):
@@ -1964,6 +1989,105 @@ class QuestionOption(models.Model):
ordering = ('position', 'id')
class Questionnaire(LoggedModel):
TYPE_ORDER_SALE = "OS"
TYPE_ORDER_POSITION_SALE = "PS"
TYPE_ORDER_POSITION_ATTENDEE_ONLY = "PA"
TYPE_ORDER_POSITION_CHECKIN = "PC"
TYPE_ORDER_POSITION_HIDDEN = "PH"
TYPE_CHOICES = (
(TYPE_ORDER_SALE, _("Order-wide, before purchase")),
(TYPE_ORDER_POSITION_SALE, _("Per product, before purchase")),
(TYPE_ORDER_POSITION_ATTENDEE_ONLY, _("Per product, via attendee link")),
(TYPE_ORDER_POSITION_CHECKIN, _("Per product, at check-in")),
(TYPE_ORDER_POSITION_HIDDEN, _("Per product, hidden")),
)
event = models.ForeignKey(
Event,
related_name="questionnaires",
on_delete=models.CASCADE
)
internal_name = models.CharField(
verbose_name=_("Internal name"),
max_length=255,
)
type = models.CharField(
max_length=5,
choices=TYPE_CHOICES,
verbose_name=_("Questionnaire type")
)
items = models.ManyToManyField(
Item,
related_name='questionnaires',
verbose_name=_("Products"),
blank=True,
help_text=_('This questionnaire will be asked to buyers of the selected products')
)
position = models.PositiveIntegerField(
default=0,
verbose_name=_("Position")
)
all_sales_channels = models.BooleanField(
verbose_name=_("Sell on all sales channels the product is sold on"),
default=True,
)
limit_sales_channels = models.ManyToManyField(
"SalesChannel",
verbose_name=_("Restrict to specific sales channels"),
help_text=_('The sales channel selection for the product as a whole takes precedence, so if a sales channel is '
'selected here but not on product level, the variation will not be available.'),
blank=True,
)
class QuestionnaireChild(LoggedModel):
SYSTEM_QUESTION_CHOICES = (
('attendee_name_parts', _('Attendee name')),
('attendee_email', _('Attendee email')),
('company', _('Company')),
('street', _('Street')),
('zipcode', _('ZIP code')),
('city', _('City')),
('country', _('Country')),
)
questionnaire = models.ForeignKey(
Questionnaire,
related_name="children",
on_delete=models.CASCADE
)
position = models.PositiveIntegerField(
default=0,
verbose_name=_("Position")
)
user_datafield = models.ForeignKey(
Question,
related_name="references",
on_delete=models.CASCADE,
null=True, blank=True,
)
system_datafield = models.CharField(
max_length=25,
choices=SYSTEM_QUESTION_CHOICES,
null=True, blank=True,
)
required = models.BooleanField(
default=False,
verbose_name=_("Required question")
)
label = I18nTextField(
verbose_name=_("Question")
)
help_text = I18nTextField(
verbose_name=_("Help text"),
help_text=_("If the question needs to be explained or clarified, do it here!"),
null=True, blank=True,
)
dependency_question = models.ForeignKey(
'QuestionnaireChild', null=True, blank=True, on_delete=models.SET_NULL, related_name='dependent_questions'
)
dependency_values = MultiStringField(default=[])
class Quota(LoggedModel):
"""
A quota is a "pool of tickets". It is there to limit the number of items
+155 -80
View File
@@ -626,42 +626,14 @@ class Order(LockModel, LoggedModel):
self.save(update_fields=['last_modified'])
def set_expires(self, now_dt=None, subevents=None):
now_dt = now_dt or now()
tz = ZoneInfo(self.event.settings.timezone)
mode = self.event.settings.get('payment_term_mode')
if mode == 'days':
exp_by_date = now_dt.astimezone(tz) + timedelta(days=self.event.settings.get('payment_term_days', as_type=int))
exp_by_date = exp_by_date.astimezone(tz).replace(hour=23, minute=59, second=59, microsecond=0)
if self.event.settings.get('payment_term_weekdays'):
if exp_by_date.weekday() == 5:
exp_by_date += timedelta(days=2)
elif exp_by_date.weekday() == 6:
exp_by_date += timedelta(days=1)
elif mode == 'minutes':
exp_by_date = now_dt.astimezone(tz) + timedelta(minutes=self.event.settings.get('payment_term_minutes', as_type=int))
else:
raise ValueError("'payment_term_mode' has an invalid value '{}'.".format(mode))
from pretix.base.services.payment import compute_payment_deadline
self.expires = exp_by_date
term_last = self.event.settings.get('payment_term_last', as_type=RelativeDateWrapper)
if term_last:
if self.event.has_subevents and subevents:
terms = [
term_last.datetime(se).date()
for se in subevents
]
if not terms:
return
term_last = min(terms)
else:
term_last = term_last.datetime(self.event).date()
term_last = make_aware(datetime.combine(
term_last,
time(hour=23, minute=59, second=59)
), tz)
if term_last < self.expires:
self.expires = term_last
self.expires = compute_payment_deadline(
event=self.event,
sales_channel=self.sales_channel,
now_dt=now_dt,
subevents=subevents,
)
@cached_property
def tax_total(self):
@@ -1304,10 +1276,9 @@ class Order(LockModel, LoggedModel):
def answerfile_name(instance, filename: str) -> str:
secret = get_random_string(length=32, allowed_chars=string.ascii_letters + string.digits)
event = (instance.cartposition if instance.cartposition else instance.orderposition.order).event
return 'cachedfiles/answers/{org}/{ev}/{secret}.{filename}'.format(
org=event.organizer.slug,
ev=event.slug,
org=instance.event.organizer.slug,
ev=instance.event.slug,
secret=secret,
filename=escape_uri_path(filename),
)
@@ -1336,6 +1307,14 @@ class QuestionAnswer(models.Model):
'CartPosition', null=True, blank=True,
related_name='answers', on_delete=models.CASCADE
)
order = models.ForeignKey(
'Order', null=True, blank=True,
related_name='answers', on_delete=models.CASCADE
)
checkoutsession = models.ForeignKey(
'CheckoutSession', null=True, blank=True,
related_name='answers', on_delete=models.CASCADE
)
question = models.ForeignKey(
Question, related_name='answers', on_delete=models.CASCADE
)
@@ -1351,16 +1330,21 @@ class QuestionAnswer(models.Model):
objects = ScopedManager(organizer='question__event__organizer')
class Meta:
unique_together = [['orderposition', 'question'], ['cartposition', 'question']]
unique_together = [
['orderposition', 'question'],
['cartposition', 'question'],
['order', 'question'],
['checkoutsession', 'question'],
]
@property
def backend_file_url(self):
if self.file:
if self.orderposition:
if self.associated_order:
return reverse('control:event.order.download.answer', kwargs={
'code': self.orderposition.order.code,
'event': self.orderposition.order.event.slug,
'organizer': self.orderposition.order.event.organizer.slug,
'code': self.associated_order.code,
'event': self.associated_order.event.slug,
'organizer': self.associated_order.event.organizer.slug,
'answer': self.pk,
})
return ""
@@ -1370,14 +1354,14 @@ class QuestionAnswer(models.Model):
from pretix.multidomain.urlreverse import eventreverse
if self.file:
if self.orderposition:
url = eventreverse(self.orderposition.order.event, 'presale:event.order.download.answer', kwargs={
'order': self.orderposition.order.code,
'secret': self.orderposition.order.secret,
if self.associated_order:
url = eventreverse(self.associated_order.event, 'presale:event.order.download.answer', kwargs={
'order': self.associated_order.code,
'secret': self.associated_order.secret,
'answer': self.pk,
})
else:
url = eventreverse(self.cartposition.event, 'presale:event.cart.download.answer', kwargs={
url = eventreverse(self.event, 'presale:event.cart.download.answer', kwargs={
'answer': self.pk,
})
@@ -1392,6 +1376,24 @@ class QuestionAnswer(models.Model):
def file_name(self):
return self.file.name.split('.', 1)[-1]
@property
def associated_order(self):
if self.orderposition:
return self.orderposition.order
elif self.order:
return self.order
@property
def event(self):
if self.orderposition:
return self.orderposition.order.event
elif self.cartposition:
return self.cartposition.event
elif self.order:
return self.order.event
elif self.checkoutsession:
return self.checkoutsession.event
def __str__(self):
return self.to_string(use_cached=True)
@@ -1447,6 +1449,12 @@ class QuestionAnswer(models.Model):
else:
return self.answer
def to_dependency_values(self):
if self.question.type in (Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE):
return [o.identifier for o in self.options.all()]
elif self.question.type in (Question.TYPE_BOOLEAN, Question.TYPE_COUNTRYCODE):
return self.answer
def save(self, *args, **kwargs):
if self.orderposition and self.cartposition:
raise ValueError('QuestionAnswer cannot be linked to an order and a cart position at the same time.')
@@ -1591,53 +1599,80 @@ class AbstractPosition(RoundingCorrectionMixin, models.Model):
def cache_answers(self, all=True):
"""
Creates two properties on the object.
(1) answ: a dictionary of question.id answer string
(2) questions: a list of Question objects, extended by an 'answer' property
Creates a new property on the object:
questions: a list of Question objects, extended by an 'answer' property
"""
self.answ = {}
for a in getattr(self, 'answerlist', self.answers.all()): # use prefetch_related cache from get_cart
self.answ[a.question_id] = a
# We need to clone our question objects, otherwise we will override the cached
# answers of other items in the same cart if the question objects have been
# selected via prefetch_related
if not all:
if hasattr(self.item, 'questions_to_ask'):
questions = list(copy.copy(q) for q in self.item.questions_to_ask)
if hasattr(self.item, 'relevant_questionnaires'):
children = list(copy.copy(qc) for qq in self.item.relevant_questionnaires for qc in qq.childlist)
else:
questions = list(copy.copy(q) for q in self.item.questions.filter(ask_during_checkin=False,
hidden=False))
children = list(copy.copy(qc) for qq in self.item.questionnaires.filter(type='PS') for qc in qq.children.all())
else:
questions = list(copy.copy(q) for q in self.item.questions.all())
children = list(copy.copy(qc) for qq in self.item.questionnaires.filter(type__startswith='P') for qc in qq.children.all())
question_cache = {
q.pk: q for q in questions
qc_cache = {
q.pk: q for q in children
}
def question_is_visible(parentid, qvals):
if parentid not in question_cache:
def qc_is_visible(parentid, qvals):
if parentid not in qc_cache:
return False
parentq = question_cache[parentid]
if parentq.dependency_question_id and not question_is_visible(parentq.dependency_question_id, parentq.dependency_values):
parentqc = qc_cache[parentid]
if parentqc.dependency_question_id and not qc_is_visible(parentqc.dependency_question_id, parentqc.dependency_values):
return False
if parentid not in self.answ:
return False
return (
('True' in qvals and self.answ[parentid].answer == 'True')
or ('False' in qvals and self.answ[parentid].answer == 'False')
or (any(qval in [o.identifier for o in self.answ[parentid].options.all()] for qval in qvals))
)
answer_values = self.get_dependency_answer_values(parentqc)
return any(qval in answer_values for qval in qvals)
self.questions = []
for q in questions:
if q.id in self.answ:
q.answer = self.answ[q.id]
q.answer.question = q # cache object
for qc in children:
if qc.user_datafield_id and qc.user_datafield_id in self.answer_cache:
qc.answer = self.answer_cache[qc.user_datafield_id]
#qc.answer.question = qc # cache object
elif qc.system_datafield:
qc.answer = self.get_system_answer(qc.system_datafield)
#qc.answer.question = qc # cache object
else:
q.answer = ""
if not q.dependency_question_id or question_is_visible(q.dependency_question_id, q.dependency_values):
self.questions.append(q)
qc.answer = ""
if not qc.dependency_question_id or qc_is_visible(qc.dependency_question_id, qc.dependency_values):
self.questions.append(qc)
@cached_property
def answer_cache(self):
return {
aw.question_id: aw for aw in getattr(self, 'answerlist', self.answers.all())
}
def get_dependency_answer_values(self, qc):
if qc.user_datafield_id:
if qc.user_datafield_id not in self.answer_cache:
return None
answer = self.answer_cache[qc.user_datafield_id]
return answer.to_dependency_values()
elif qc.system_datafield:
return [self.get_system_answer(qc.system_datafield)]
else:
raise ValueError('Questionnaire child without datafield has no answer')
def get_system_answer(self, system_datafield_name):
if system_datafield_name == 'attendee_name_parts':
return self.attendee_name_parts
elif system_datafield_name == 'attendee_email':
return self.attendee_email
elif system_datafield_name == 'street':
return self.street
elif system_datafield_name == 'zipcode':
return self.zipcode
elif system_datafield_name == 'city':
return self.city
elif system_datafield_name == 'state':
return self.state
elif system_datafield_name == 'country':
return self.country
else:
raise ValueError('Unknown system question name')
@property
def net_price(self):
@@ -3177,6 +3212,39 @@ class Transaction(models.Model):
return self.tax_value_includes_rounding_correction * self.count
class CheckoutSession(models.Model):
"""
A checkout session optionally bundles cart positions with additional information. This is historically
not required in pretix and currently only used in the Storefront API.
"""
event = models.ForeignKey(
Event,
verbose_name=_("Event"),
related_name="checkout_sessions",
on_delete=models.CASCADE,
)
cart_id = models.CharField(
max_length=255, unique=True,
verbose_name=_("Cart ID (e.g. session key)"),
)
created = models.DateTimeField(
verbose_name=_("Date"),
auto_now_add=True,
)
customer = models.ForeignKey(
Customer,
related_name='checkout_sessions',
null=True, blank=True,
on_delete=models.SET_NULL,
)
sales_channel = models.ForeignKey(
"SalesChannel",
on_delete=models.CASCADE,
)
testmode = models.BooleanField(default=False)
session_data = models.JSONField(default=dict)
class CartPosition(AbstractPosition):
"""
A cart position is similar to an order line, except that it is not
@@ -3381,6 +3449,13 @@ class CartPosition(AbstractPosition):
class InvoiceAddress(models.Model):
last_modified = models.DateTimeField(auto_now=True)
checkout_session = models.OneToOneField(
CheckoutSession,
null=True,
blank=True,
related_name='invoice_address',
on_delete=models.CASCADE
)
order = models.OneToOneField(Order, null=True, blank=True, related_name='invoice_address', on_delete=models.CASCADE)
customer = models.ForeignKey(
Customer,
+24 -7
View File
@@ -72,7 +72,7 @@ from pretix.helpers.countries import CachedCountries
from pretix.helpers.format import format_map
from pretix.helpers.money import DecimalTextInput
from pretix.multidomain.urlreverse import eventreverse_absolute
from pretix.presale.views import get_cart
from pretix.presale.views import get_cart_positions
from pretix.presale.views.cart import cart_session, get_or_create_cart_id
logger = logging.getLogger(__name__)
@@ -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'])
@@ -1148,7 +1165,7 @@ class FreeOrderProvider(BasePaymentProvider):
def is_allowed(self, request: HttpRequest, total: Decimal=None) -> bool:
from .services.cart import get_fees
cart = get_cart(request)
cart = get_cart_positions(request)
try:
fees = get_fees(event=request.event, request=request,
@@ -1416,7 +1433,7 @@ class GiftCardPayment(BasePaymentProvider):
for p in cs.get('payments', [])
if p.get('info_data', {}).get('gift_card')
]
positions = get_cart(request)
positions = get_cart_positions(request)
testmode = self.event.testmode
else:
used_cards = []
+16 -25
View File
@@ -1232,9 +1232,7 @@ class Renderer:
for i, page in enumerate(fg_pdf.pages):
bg_page = self.bg_pdf.pages[i]
_correct_page_media_box(bg_page)
page.merge_page(bg_page, over=False)
output.add_page(page)
_merge_with_correct_page_media_box(output, page, bg_page)
# pdf_header is a string like "%pdf-X.X"
if float(self.bg_pdf.pdf_header[5:]) > float(fg_pdf.pdf_header[5:]):
@@ -1299,39 +1297,32 @@ def merge_background(fg_pdf: PdfWriter, bg_pdf: PdfWriter, out_file, compress):
bg_pdf.write(bg_filename)
subprocess.run(pdftk_cmd, check=True, stdout=out_file)
else:
output = PdfWriter()
for i, page in enumerate(fg_pdf.pages):
bg_page = bg_pdf.pages[i]
_correct_page_media_box(bg_page)
page.merge_page(bg_page, over=False)
_merge_with_correct_page_media_box(output, page, bg_page)
# pdf_header is a string like "%pdf-X.X"
if float(bg_pdf.pdf_header[5:]) > float(fg_pdf.pdf_header[5:]):
fg_pdf.pdf_header = bg_pdf.pdf_header
fg_pdf.write(out_file)
output.pdf_header = (
bg_pdf.pdf_header
if float(bg_pdf.pdf_header[5:]) > float(fg_pdf.pdf_header[5:])
else fg_pdf.pdf_header
)
output.write(out_file)
def _correct_page_media_box(page: pypdf.PageObject):
if page.rotation != 0:
page.transfer_rotation_to_content()
media_box = page.mediabox
def _merge_with_correct_page_media_box(output: pypdf.PdfWriter, fg_page: pypdf.PageObject, bg_page: pypdf.PageObject):
if bg_page.rotation != 0:
bg_page.transfer_rotation_to_content()
media_box = bg_page.mediabox
trsf = pypdf.Transformation()
if media_box.bottom != 0:
trsf = trsf.translate(0, -media_box.bottom)
if media_box.left != 0:
trsf = trsf.translate(-media_box.left, 0)
page.add_transformation(trsf, False)
for b in ["/MediaBox", "/CropBox", "/BleedBox", "/TrimBox", "/ArtBox"]:
if b in page:
rr = pypdf.generic.RectangleObject(page[b])
pt1 = trsf.apply_on(rr.lower_left)
pt2 = trsf.apply_on(rr.upper_right)
page[pypdf.generic.NameObject(b)] = pypdf.generic.RectangleObject((
min(pt1[0], pt2[0]),
min(pt1[1], pt2[1]),
max(pt1[0], pt2[0]),
max(pt1[1], pt2[1]),
))
fg_page = output.add_page(fg_page)
fg_page.merge_transformed_page(bg_page, trsf, over=False, expand=False)
@deconstructible
+373 -155
View File
@@ -19,30 +19,245 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
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)
+13 -1
View File
@@ -61,7 +61,7 @@ from pretix.base.models import (
Seat, SeatCategoryMapping, Voucher,
)
from pretix.base.models.event import SubEvent
from pretix.base.models.orders import OrderFee
from pretix.base.models.orders import CheckoutSession, OrderFee
from pretix.base.models.tax import TaxRule
from pretix.base.reldate import RelativeDateWrapper
from pretix.base.services.checkin import _save_answers
@@ -472,6 +472,16 @@ class CartManager:
if term_last < time_machine_now(self.real_now_dt):
raise CartError(error_messages['payment_ended'])
def _ensure_checkout_session(self):
CheckoutSession.objects.get_or_create(
event=self.event,
cart_id=self.cart_id,
defaults={
"sales_channel": self._sales_channel,
"testmode": self.event.testmode,
},
)
def _extend_expiry_of_valid_existing_positions(self):
# real_now_dt is initialized at CartManager instantiation, so it's slightly in the past. Add a small
# delta to reduce risk of extending already expired CartPositions.
@@ -1559,6 +1569,7 @@ class CartManager:
def commit(self):
self._check_presale_dates()
self._ensure_checkout_session()
self._check_max_cart_size()
err = self._delete_out_of_timeframe()
@@ -1594,6 +1605,7 @@ def add_payment_to_cart_session(cart_session, provider, min_value: Decimal=None,
'max_value': str(max_value) if max_value is not None else None,
'info_data': info_data or {},
})
cart_session['payments_postpone'] = False
def add_payment_to_cart(request, provider, min_value: Decimal=None, max_value: Decimal=None, info_data: dict=None):
+5 -3
View File
@@ -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 (
@@ -891,6 +892,7 @@ def _save_answers(op, answers, given_answers):
qa.answer = answer
qa.save(update_fields=['answer'])
qa.options.clear()
return qa
written = False
for q, a in given_answers.items():
@@ -1043,10 +1045,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)
+5
View File
@@ -33,6 +33,7 @@ from pretix.base.models.customers import CustomerSSOGrant
from ..models import CachedFile, CartPosition, InvoiceAddress
from ..models.auth import UserKnownLoginSource
from ..models.orders import CheckoutSession
from ..signals import periodic_task
@@ -43,6 +44,10 @@ def clean_cart_positions(sender, **kwargs):
cp.delete()
for cp in CartPosition.objects.filter(expires__lt=now() - timedelta(days=14), addon_to__isnull=True):
cp.delete()
for cs in CheckoutSession.objects.filter(created__lt=now() - timedelta(days=14)).exclude(
Exists(CartPosition.objects.filter(cart_id=OuterRef("cart_id")))
):
cs.delete()
for ia in InvoiceAddress.objects.filter(order__isnull=True, customer__isnull=True, last_modified__lt=now() - timedelta(days=14)):
ia.delete()
+138 -87
View File
@@ -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,10 +71,10 @@ 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,
BlockedTicketSecret, CheckoutSession, InvoiceAddress, OrderFee,
OrderRefund, generate_secret,
)
from pretix.base.models.organizer import SalesChannel, TeamAPIToken
from pretix.base.models.tax import TAXED_ZERO, TaxedPrice, TaxRule
@@ -961,7 +961,7 @@ def _check_positions(event: Event, now_dt: datetime, time_machine_now_dt: dateti
def _apply_rounding_and_fees(positions: List[CartPosition], payment_requests: List[dict], address: InvoiceAddress,
meta_info: dict, event: Event, require_approval=False):
meta_info: dict, event: Event, sales_channel: SalesChannel, require_approval=False):
fees = []
# Pre-rounding, pre-fee total is used for fee calculation
total = sum([c.gross_price_before_rounding for c in positions])
@@ -1021,7 +1021,14 @@ def _apply_rounding_and_fees(positions: List[CartPosition], payment_requests: Li
payments_assigned += to_pay
p['payment_amount'] = to_pay
if total != payments_assigned and not require_approval:
allow_postponed_payment = (
require_approval or
(
sales_channel.identifier in event.settings.payment_choice_postpone_allowed_channels and not payment_requests
)
)
if total != payments_assigned and not allow_postponed_payment:
raise OrderError(_("The selected payment methods do not cover the total balance."))
return fees
@@ -1030,7 +1037,8 @@ def _apply_rounding_and_fees(positions: List[CartPosition], payment_requests: Li
def _create_order(event: Event, *, email: str, positions: List[CartPosition], now_dt: datetime,
payment_requests: List[dict], sales_channel: SalesChannel, locale: str=None,
address: InvoiceAddress=None, meta_info: dict=None, shown_total=None,
customer=None, valid_if_pending=False, api_meta: dict=None, tax_rounding_mode=None):
customer=None, valid_if_pending=False, api_meta: dict=None, tax_rounding_mode=None,
cart_id: str=None):
payments = []
try:
@@ -1042,7 +1050,15 @@ def _create_order(event: Event, *, email: str, positions: List[CartPosition], no
# Final calculation of fees, also performs final rounding
try:
fees = _apply_rounding_and_fees(positions, payment_requests, address, meta_info, event, require_approval=require_approval)
fees = _apply_rounding_and_fees(
positions,
payment_requests,
address,
meta_info,
event,
sales_channel=sales_channel,
require_approval=require_approval
)
except TaxRule.SaleNotAllowed:
raise OrderError(error_messages['country_blocked'])
@@ -1113,6 +1129,14 @@ def _create_order(event: Event, *, email: str, positions: List[CartPosition], no
if meta_info:
for msg in meta_info.get('confirm_messages', []):
order.log_action('pretix.event.order.consent', data={'msg': msg})
if cart_id:
try:
session = CheckoutSession.objects.get(event=event, cart_id=cart_id)
except CheckoutSession.DoesNotExist:
pass
else:
session.answers.update(order=order, checkoutsession=None)
session.delete()
order_placed.send(event, order=order, bulk=False)
return order, payments
@@ -1160,7 +1184,7 @@ def _order_placed_email_attendee(event: Event, order: Order, position: OrderPosi
def _perform_order(event: Event, payment_requests: List[dict], position_ids: List[str],
email: str, locale: str, address: int, meta_info: dict=None, sales_channel: str='web',
shown_total=None, customer=None, api_meta: dict=None, tax_rounding_mode=None):
shown_total=None, customer=None, api_meta: dict=None, tax_rounding_mode=None, cart_id: str=None):
for p in payment_requests:
p['pprov'] = event.get_payment_providers(cached=True)[p['provider']]
if not p['pprov']:
@@ -1267,6 +1291,7 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
valid_if_pending=valid_if_pending,
api_meta=api_meta,
tax_rounding_mode=tax_rounding_mode,
cart_id=cart_id,
)
try:
@@ -1494,83 +1519,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=[]):
@@ -3169,12 +3215,12 @@ class OrderChangeManager:
def perform_order(self, event: Event, payments: List[dict], positions: List[str],
email: str=None, locale: str=None, address: int=None, meta_info: dict=None,
sales_channel: str='web', shown_total=None, customer=None, override_now_dt: datetime=None,
api_meta: dict=None):
api_meta: dict=None, cart_id: str=None):
with language(locale), time_machine_now_assigned(override_now_dt):
try:
try:
return _perform_order(event, payments, positions, email, locale, address, meta_info,
sales_channel, shown_total, customer, api_meta)
sales_channel, shown_total, customer, api_meta, cart_id=cart_id)
except LockTimeoutException:
self.retry()
except (MaxRetriesExceededError, LockTimeoutException):
@@ -3438,7 +3484,7 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
}
)
new_invoice_created = False
new_invoice = None
if recreate_invoices:
# Lock to prevent duplicate invoice creation
order = Order.objects.select_for_update(of=OF_SELF).get(pk=order.pk)
@@ -3449,13 +3495,16 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
if has_active_invoice and order.total != oldtotal:
try:
generate_cancellation(i)
generate_invoice(order)
new_invoice = generate_invoice(order)
except Exception as e:
logger.exception("Could not generate invoice.")
order.log_action("pretix.event.order.invoice.failed", data={
"exception": str(e)
})
new_invoice_created = True
else:
order.log_action('pretix.event.order.invoice.generated', data={
'invoice': new_invoice.pk
})
elif (not has_active_invoice or order.invoice_dirty) and invoice_qualified(order):
if order.event.settings.get('invoice_generate') == 'True' or (
@@ -3465,10 +3514,9 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
try:
if has_active_invoice:
generate_cancellation(i)
i = generate_invoice(order)
new_invoice_created = True
new_invoice = generate_invoice(order)
order.log_action('pretix.event.order.invoice.generated', data={
'invoice': i.pk
'invoice': new_invoice.pk
})
except Exception as e:
logger.exception("Could not generate invoice.")
@@ -3476,8 +3524,11 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
"exception": str(e)
})
if new_invoice and invoice_transmission_separately(new_invoice):
transmit_invoice.apply_async(args=(order.event_id, new_invoice.pk, False))
order.create_transactions()
return old_fee, new_fee, fee, new_payment, new_invoice_created
return old_fee, new_fee, fee, new_payment, bool(new_invoice)
@receiver(order_paid, dispatch_uid="pretixbase_order_paid_giftcards")
+76
View File
@@ -0,0 +1,76 @@
#
# 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 <https://pretix.eu/about/en/license>.
#
# 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
# <https://www.gnu.org/licenses/>.
#
from datetime import datetime, time, timedelta
from zoneinfo import ZoneInfo
from django.utils.timezone import make_aware, now
from pretix.base.models import Event, SalesChannel
from pretix.base.reldate import RelativeDateWrapper
def compute_payment_deadline(event: Event, sales_channel: SalesChannel, now_dt=None, subevents=None) -> datetime:
now_dt = now_dt or now()
tz = ZoneInfo(event.settings.timezone)
sales_channel_suffix = "_" + sales_channel.identifier.replace(".", "_")
if not (mode := event.settings.get(f'payment_term_mode{sales_channel_suffix}')):
mode = event.settings.get('payment_term_mode')
sales_channel_suffix = ""
if mode == 'days':
exp_by_date = now_dt.astimezone(tz) + timedelta(
days=event.settings.get(f'payment_term_days{sales_channel_suffix}', as_type=int))
exp_by_date = exp_by_date.astimezone(tz).replace(hour=23, minute=59, second=59, microsecond=0)
if event.settings.get('payment_term_weekdays'):
if exp_by_date.weekday() == 5:
exp_by_date += timedelta(days=2)
elif exp_by_date.weekday() == 6:
exp_by_date += timedelta(days=1)
elif mode == 'minutes':
exp_by_date = now_dt.astimezone(tz) + timedelta(
minutes=event.settings.get(f'payment_term_minutes{sales_channel_suffix}', as_type=int))
else:
raise ValueError("'payment_term_mode' has an invalid value '{}'.".format(mode))
expires = exp_by_date
term_last = event.settings.get('payment_term_last', as_type=RelativeDateWrapper)
if term_last:
if event.has_subevents and subevents:
terms = [
term_last.datetime(se).date()
for se in subevents
]
if not terms:
return expires
term_last = min(terms)
else:
term_last = term_last.datetime(event).date()
term_last = make_aware(datetime.combine(
term_last,
time(hour=23, minute=59, second=59)
), tz)
if term_last < expires:
return term_last
return expires
+16
View File
@@ -561,6 +561,22 @@ def base_placeholders(sender, **kwargs):
'event_location', ['event_or_subevent'], lambda event_or_subevent: str(event_or_subevent.location or ''),
lambda event: str(event.location or ''),
),
SimpleFunctionalTextPlaceholder(
'event_begin_time', ['event_or_subevent'],
lambda event_or_subevent:
date_format(event_or_subevent.date_from.astimezone(event_or_subevent.timezone), 'TIME_FORMAT')
if event_or_subevent.date_from
else '',
lambda event: date_format(event.date_from.astimezone(event.timezone), 'TIME_FORMAT') if event.date_from else '',
),
SimpleFunctionalTextPlaceholder(
'event_end_time', ['event_or_subevent'],
lambda event_or_subevent:
date_format(event_or_subevent.date_to.astimezone(event_or_subevent.timezone), 'TIME_FORMAT')
if event_or_subevent.date_to
else '',
lambda event: date_format(event.date_to.astimezone(event.timezone), 'TIME_FORMAT') if event.date_to else '',
),
SimpleFunctionalTextPlaceholder(
'event_admission_time', ['event_or_subevent'],
lambda event_or_subevent:
+1 -1
View File
@@ -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:
+68 -2
View File
@@ -343,6 +343,66 @@ def _validate_vat_id_EU(vat_id, country_code):
return vat_id
def _validate_vat_id_EU_fallback_germany(vat_id, country_code, requester_id):
# We can skip most static validation checks because _validate_vat_id_EU always runs before
vat_id = normalize_vat_id(vat_id, country_code)
# The VIES service of the European commission is overused and down due to rate limits A LOT. There is another
# API by German BZSt, but it only works if the requester is German and the requested is not.
# https://www.bzst.de/DE/Unternehmen/Identifikationsnummern/Umsatzsteuer-Identifikationsnummer/AuslaendischeUSt-IdNr/auslaendische_ust_idnr_node.html
try:
r = requests.post(
"https://api.evatr.vies.bzst.de/app/v1/abfrage",
json={
"anfragendeUstid": requester_id,
"angefragteUstid": vat_id,
},
timeout=10,
)
d = r.json()
if r.status_code == 200:
if d['status'] in ('evatr-0000', 'evatr-2008'):
# evatr-0000: Die angefragte Ust-IdNr. ist zum Anfragezeitpunkt gültig.
# evatr-2008: Die angefragte Ust-IdNr. ist zum Anfragezeitpunkt gültig.
# Für die qualifizierte Bestätigungsanfrage liegt einer Besonderheit vor.
# Für Rückfragen wenden Sie sich an das BZSt.
return vat_id
# evatr-2002: Die angefragte USt-IdNr. ist zum Anfragezeitpunkt nicht gültig.
# Sie ist erst gültig ab dem Datum im Feld gueltigAb.
# evatr-2006: Die angefragte Ust-IdNr. ist zum Anfragezeitpunkt nicht gültig.
# Sie war gültig im Zeitraum, der durch die Werte in den Feldern gueltigAb und gueltigBis beschrieben ist.
raise VATIDFinalError(error_messages['invalid'])
elif r.status_code == 400:
if d['status'] in ('evatr-0002', 'evatr-0004', 'evatr-0008'):
# evatr-0002: Mindestens eins der Pflichtfelder ist nicht besetzt.
# evatr-0004: Die anfragende DE Ust-IdNr. ist syntaktisch falsch. Sie passt nicht in das deutsche Erzeugungsschema.
# evatr-0008: Die maximale Anzahl von qualifizierten Bestätigungsabfragen für diese Session wurde erreicht.
# Bitte starten Sie erneut mit einer einfachen Bestätigungsabfrage.
raise VATIDTemporaryError(error_messages['unavailable'])
# evatr-0005: Die angegebene angefragte Ust-IdNr. ist syntaktisch falsch.
# evatr-0012: Die angefrage USt-IdNr. ist syntaktisch falsch. Sie passt nicht in das Erzeugungsschema.
# evatr-2003: Das angegebene Länderkennzeichen der angefragten USt-IdNr. ist nicht gültig.
raise VATIDFinalError(error_messages['invalid'])
elif r.status_code == 403:
# evatr-0006: Die anfragende DE USt-IdNr. ist nicht berechtigt eine DE Ust-IdNr. anzufragen.
# evatr-0007: Fehlerhafter Aufruf.
raise VATIDTemporaryError(error_messages['unavailable'])
elif r.status_code == 404:
if d['status'] in ('evatr-2005'):
# evatr-2005: Die angegebene eigene DE Ust-IdNr. ist zum Anfragezeitpunkt nicht gültig.
raise VATIDTemporaryError(error_messages['unavailable'])
# evatr-2001: Die angefragte USt-IdNr. ist zum Anfragezeitpunkt nicht vergeben.
raise VATIDFinalError(error_messages['invalid'])
else: # 500, 503
raise VATIDTemporaryError(error_messages['unavailable'])
except requests.RequestException:
logger.exception('VAT ID checking failed for country {}'.format(country_code))
raise VATIDTemporaryError(error_messages['unavailable'])
except ValueError: # JSON parsing failed
logger.exception('VAT ID checking failed for country {}'.format(country_code))
raise VATIDTemporaryError(error_messages['unavailable'])
def _validate_vat_id_CH(vat_id, country_code):
if vat_id[:3] != 'CHE':
raise VATIDFinalError(error_messages['country_mismatch'])
@@ -394,12 +454,18 @@ def _validate_vat_id_CH(vat_id, country_code):
return vat_id
def validate_vat_id(vat_id, country_code):
def validate_vat_id(vat_id, country_code, requester_id=None):
if not vat_id:
return vat_id
country_code = str(country_code)
if is_eu_country(country_code):
return _validate_vat_id_EU(vat_id, country_code)
try:
return _validate_vat_id_EU(vat_id, country_code)
except VATIDTemporaryError:
if requester_id and requester_id.startswith("DE") and not vat_id.startswith("DE"):
return _validate_vat_id_EU_fallback_germany(vat_id, country_code, requester_id)
else:
raise
elif country_code == 'CH':
return _validate_vat_id_CH(vat_id, country_code)
elif country_code == 'NO':
File diff suppressed because one or more lines are too long
+138 -86
View File
@@ -27,23 +27,26 @@ from decimal import Decimal
from django import forms
from django.core.files.uploadedfile import UploadedFile
from django.db import IntegrityError
from django.db.models import Prefetch, QuerySet
from django.db.models import Prefetch, Q, QuerySet
from django.utils.functional import cached_property
from django.utils.timezone import make_aware
from pretix.base.forms.questions import (
BaseInvoiceAddressForm, BaseInvoiceNameForm, BaseQuestionsForm,
BaseInvoiceAddressForm, BaseInvoiceNameForm, OrderLevelQuestionsForm,
TicketLevelQuestionsForm,
)
from pretix.base.models import (
CartPosition, InvoiceAddress, OrderPosition, Question, QuestionAnswer,
QuestionOption,
QuestionnaireChild, QuestionOption,
)
from pretix.base.models.customers import AttendeeProfile
from pretix.base.models.orders import CheckoutSession, Order
from pretix.presale.signals import contact_form_fields_overrides
class BaseQuestionsViewMixin:
form_class = BaseQuestionsForm
order_form_class = OrderLevelQuestionsForm
orderposition_form_class = TicketLevelQuestionsForm
all_optional = False
@cached_property
@@ -56,6 +59,28 @@ class BaseQuestionsViewMixin:
def question_form_kwargs(self, cr):
return {}
@property
def order_question_container(self):
raise NotImplementedError()
@cached_property
def order_questions_form(self):
container = self.order_question_container
if container is None:
return None
kwargs = {} # self.question_form_kwargs(cr)
form = self.order_form_class(
event=self.request.event,
prefix='order',
request=self.request,
container=container,
all_optional=self.all_optional,
data=(self.request.POST if self.request.method == 'POST' else None),
files=(self.request.FILES if self.request.method == 'POST' else None),
**kwargs
)
return form
@cached_property
def forms(self):
"""
@@ -69,15 +94,17 @@ class BaseQuestionsViewMixin:
orderpos = cr if isinstance(cr, OrderPosition) else None
kwargs = self.question_form_kwargs(cr)
form = self.form_class(event=self.request.event,
prefix=cr.id,
request=self.request,
cartpos=cartpos,
orderpos=orderpos,
all_optional=self.all_optional,
data=(self.request.POST if self.request.method == 'POST' else None),
files=(self.request.FILES if self.request.method == 'POST' else None),
**kwargs)
form = self.orderposition_form_class(
event=self.request.event,
prefix=cr.id,
request=self.request,
cartpos=cartpos,
orderpos=orderpos,
all_optional=self.all_optional,
data=(self.request.POST if self.request.method == 'POST' else None),
files=(self.request.FILES if self.request.method == 'POST' else None),
**kwargs
)
form.pos = cartpos or orderpos
form.show_copy_answers_to_addon_button = form.pos.addon_to and (
set(form.pos.addon_to.item.questions.all()) & set(form.pos.item.questions.all()) or
@@ -108,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)
@@ -130,8 +159,38 @@ class BaseQuestionsViewMixin:
def save(self):
failed = False
if self.order_questions_form:
if not self.order_questions_form.is_valid():
failed = True
else:
checkoutsession = self.order_question_container if isinstance(self.order_question_container, CheckoutSession) else None
order = self.order_question_container if isinstance(self.order_question_container, Order) else None
for k, v in self.order_questions_form.cleaned_data.items():
if k.startswith('question_'):
field = self.order_questions_form.fields[k]
if hasattr(field, 'answer'):
# We already have a cached answer object, so we don't
# have to create a new one
if v == '' or v is None or (isinstance(field, forms.FileField) and v is False) \
or (isinstance(v, QuerySet) and not v.exists()):
if field.answer.file:
field.answer.file.delete()
field.answer.delete()
else:
self._save_to_answer(field, field.answer, v)
field.answer.save()
elif v != '' and v is not None:
self._upsert_answer(
field, v,
checkoutsession=checkoutsession,
order=order,
question=field.question,
)
for form in self.forms:
meta_info = form.pos.meta_info_data
cartposition = form.pos if isinstance(form.pos, CartPosition) else None
orderposition = form.pos if isinstance(form.pos, OrderPosition) else None
# Every form represents a CartPosition or OrderPosition with questions attached
if not form.is_valid():
failed = True
@@ -140,10 +199,8 @@ class BaseQuestionsViewMixin:
prof = AttendeeProfile.objects.filter(
customer=self.cart_customer, pk=form.cleaned_data.get('saved_id')
).first() or AttendeeProfile(customer=getattr(self, 'cart_customer', None))
answers_key_to_index = {a.get('field_name'): i for i, a in enumerate(prof.answers)}
else:
prof = AttendeeProfile(customer=getattr(self, 'cart_customer', None))
answers_key_to_index = {}
# This form was correctly filled, so we store the data as
# answers to the questions / in the CartPosition object
@@ -181,64 +238,19 @@ class BaseQuestionsViewMixin:
else:
self._save_to_answer(field, field.answer, v)
field.answer.save()
if isinstance(field, forms.ModelMultipleChoiceField) or isinstance(field, forms.ModelChoiceField):
answer_value = {o.identifier: str(o) for o in field.answer.options.all()}
elif isinstance(field, forms.BooleanField):
answer_value = bool(field.answer.answer)
else:
answer_value = str(field.answer.answer)
answer_dict = {
'field_name': k,
'field_label': str(field.label),
'value': answer_value,
'question_type': field.question.type,
'question_identifier': field.question.identifier,
}
if k in answers_key_to_index:
prof.answers[answers_key_to_index[k]] = answer_dict
else:
prof.answers.append(answer_dict)
answer_dict = self._build_answer_dict(field, field.answer, k)
prof.store_answer(answer_dict)
elif v != '' and v is not None:
answer = QuestionAnswer(
cartposition=(form.pos if isinstance(form.pos, CartPosition) else None),
orderposition=(form.pos if isinstance(form.pos, OrderPosition) else None),
answer = self._upsert_answer(
field, v,
cartposition=cartposition,
orderposition=orderposition,
question=field.question,
)
try:
self._save_to_answer(field, answer, v)
answer.save()
except IntegrityError:
# Since we prefill ``field.answer`` at form creation time, there's a possible race condition
# here if the users submits their save request a second time while the first one is still running,
# thus leading to duplicate QuestionAnswer objects. Since Django doesn't support UPSERT, the "proper"
# fix would be a transaction with select_for_update(), or at least fetching using get_or_create here
# again. However, both of these approaches have a significant performance overhead for *all* requests,
# while the issue happens very very rarely. So we opt for just catching the error and retrying properly.
answer = QuestionAnswer.objects.get(
cartposition=(form.pos if isinstance(form.pos, CartPosition) else None),
orderposition=(form.pos if isinstance(form.pos, OrderPosition) else None),
question=field.question,
)
self._save_to_answer(field, answer, v)
answer.save()
if isinstance(field, forms.ModelMultipleChoiceField) or isinstance(field, forms.ModelChoiceField):
answer_value = {o.identifier: str(o) for o in answer.options.all()}
elif isinstance(field, forms.BooleanField):
answer_value = bool(answer.answer)
else:
answer_value = str(answer.answer)
answer_dict = {
'field_name': k,
'field_label': str(field.label),
'value': answer_value,
'question_type': field.question.type,
'question_identifier': field.question.identifier,
}
if k in answers_key_to_index:
prof.answers[answers_key_to_index[k]] = answer_dict
else:
prof.answers.append(answer_dict)
answer_dict = self._build_answer_dict(field, answer, k)
prof.store_answer(answer_dict)
else:
field = form.fields[k]
@@ -257,10 +269,7 @@ class BaseQuestionsViewMixin:
'question_type': None,
'question_identifier': None,
}
if k in answers_key_to_index:
prof.answers[answers_key_to_index[k]] = answer_dict
else:
prof.answers.append(answer_dict)
prof.store_answer(answer_dict)
form.pos.meta_info = json.dumps(meta_info)
form.pos.save()
@@ -271,6 +280,23 @@ class BaseQuestionsViewMixin:
return not failed
def _upsert_answer(self, field, v, **answer_kwargs):
answer = QuestionAnswer(**answer_kwargs)
try:
self._save_to_answer(field, answer, v)
answer.save()
except IntegrityError:
# Since we prefill ``field.answer`` at form creation time, there's a possible race condition
# here if the users submits their save request a second time while the first one is still running,
# thus leading to duplicate QuestionAnswer objects. Since Django doesn't support UPSERT, the "proper"
# fix would be a transaction with select_for_update(), or at least fetching using get_or_create here
# again. However, both of these approaches have a significant performance overhead for *all* requests,
# while the issue happens very very rarely. So we opt for just catching the error and retrying properly.
answer = QuestionAnswer.objects.get(**answer_kwargs)
self._save_to_answer(field, answer, v)
answer.save()
return answer
def _save_to_answer(self, field, answer, value):
if isinstance(field, forms.ModelMultipleChoiceField):
answstr = ", ".join([str(o) for o in value])
@@ -294,6 +320,21 @@ class BaseQuestionsViewMixin:
else:
answer.answer = value
def _build_answer_dict(self, field, answer, k):
if isinstance(field, forms.ModelMultipleChoiceField) or isinstance(field, forms.ModelChoiceField):
answer_value = {o.identifier: str(o) for o in answer.options.all()}
elif isinstance(field, forms.BooleanField):
answer_value = bool(answer.answer)
else:
answer_value = str(answer.answer)
return {
'field_name': k,
'field_label': str(field.label),
'value': answer_value,
'question_type': field.question.type,
'question_identifier': field.question.identifier,
}
class OrderQuestionsViewMixin(BaseQuestionsViewMixin):
invoice_form_class = BaseInvoiceAddressForm
@@ -307,27 +348,37 @@ class OrderQuestionsViewMixin(BaseQuestionsViewMixin):
@cached_property
def positions(self):
qqs = self.request.event.questions.all()
qqs = self.request.event.questionnaires.all()
if self.only_user_visible:
qqs = qqs.filter(ask_during_checkin=False, hidden=False)
qqs = qqs.filter(type='PS')
else:
qqs = qqs.filter(type__startswith='P')
qqs = qqs.filter(
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.order.sales_channel.identifier)
)
return list(self.order.positions.select_related(
'item', 'variation'
).prefetch_related(
Prefetch('answers',
QuestionAnswer.objects.prefetch_related('options'),
to_attr='answerlist'),
Prefetch('item__questions',
Prefetch('item__questionnaires',
qqs.prefetch_related(
Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch(
# This prefetch statement is utter bullshit, but it actually prevents Django from doing
# a lot of queries since ModelChoiceIterator stops trying to be clever once we have
# a prefetch lookup on this query...
'question',
Question.objects.none(),
to_attr='dummy'
)))
).select_related('dependency_question'),
to_attr='questions_to_ask')
Prefetch('children', QuestionnaireChild.objects.prefetch_related(
Prefetch('user_datafield', Question.objects.prefetch_related(
Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch(
# This prefetch statement is utter bullshit, but it actually prevents Django from doing
# a lot of queries since ModelChoiceIterator stops trying to be clever once we have
# a prefetch lookup on this query...
'question',
Question.objects.none(),
to_attr='dummy'
)))
))
),
to_attr='childlist')
),
to_attr='relevant_questionnaires')
))
@cached_property
@@ -397,6 +448,7 @@ class OrderQuestionsViewMixin(BaseQuestionsViewMixin):
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['order'] = self.order
ctx['order_questions_form'] = self.order_questions_form
ctx['formgroups'] = self.formdict.items()
ctx['invoice_form'] = self.invoice_form
ctx['invoice_address_asked'] = self.address_asked
+2
View File
@@ -26,6 +26,7 @@ from django.core import signing
from django.http import HttpResponseBadRequest, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.utils.html import format_html
logger = logging.getLogger(__name__)
@@ -60,6 +61,7 @@ def redir_view(request):
u = urllib.parse.urlparse(url)
return render(request, 'pretixbase/redirect.html', {
'hostname': u.hostname,
'bold_hostname': format_html("<strong>{}</strong>", u.hostname),
'url': url,
})
+12 -1
View File
@@ -172,7 +172,9 @@ class CachedFileInput(forms.ClearableFileInput):
from ...base.models import CachedFile
v = super().value_from_datadict(data, files, name)
if v is None and data.get(name + '-cachedfile'): # An explicit "[x] clear" would be False, not None
return CachedFile.objects.filter(id=data[name + '-cachedfile']).first()
v = CachedFile.objects.filter(id=data[name + '-cachedfile']).first()
if not v.allowed_for_session(self.request):
v = None
return v
def get_context(self, name, value, attrs):
@@ -244,6 +246,11 @@ class ExtFileField(ExtValidationMixin, SizeFileField):
class CachedFileField(ExtFileField):
widget = CachedFileInput
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request", None)
super().__init__(*args, **kwargs)
self.widget.request = self.request
def to_python(self, data):
from ...base.models import CachedFile
@@ -271,6 +278,8 @@ class CachedFileField(ExtFileField):
filename=data.name,
type=data.content_type,
)
if self.request:
cf.bind_to_session(self.request) # no salt because we want direct web access
cf.file.save(data.name, data.file)
cf.save()
data._uploaded_to = cf
@@ -294,6 +303,8 @@ class CachedFileField(ExtFileField):
filename=data.name,
type=data.content_type,
)
if self.request:
cf.bind_to_session(self.request) # no salt because we want direct web access
cf.file.save(data.name, data.file)
cf.save()
data._uploaded_to = cf
+53
View File
@@ -600,6 +600,7 @@ class EventSettingsForm(EventSettingsValidationMixin, FormPlaceholderMixin, Sett
'imprint_url',
'checkout_email_helptext',
'presale_has_ended_text',
'event_list_empty_text',
'voucher_explanation_text',
'checkout_success_text',
'show_dates_on_frontpage',
@@ -734,6 +735,7 @@ class EventSettingsForm(EventSettingsValidationMixin, FormPlaceholderMixin, Sett
del self.fields['event_list_available_only']
del self.fields['event_list_filters']
del self.fields['event_calendar_future_only']
del self.fields['event_list_empty_text']
self.fields['primary_font'].choices = [('Open Sans', 'Open Sans')] + sorted([
(a, FontSelect.FontOption(title=a, data=v)) for a, v in get_fonts(self.event, pdf_support_required=False).items()
], key=lambda a: a[0])
@@ -853,9 +855,60 @@ class PaymentSettingsForm(EventSettingsValidationMixin, SettingsForm):
'payment_term_accept_late',
'payment_pending_hidden',
'payment_explanation',
'payment_choice_postpone_allowed_channels',
'tax_rule_payment',
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
channels = list(self.obj.organizer.sales_channels.all())
self.fields['payment_choice_postpone_allowed_channels'].choices = [
(c.identifier, c.label) for c in channels
if c.type_instance.payment_restrictions_supported
]
self.term_channel_fields = {}
for c in channels:
if c.type_instance.payment_restrictions_supported and c.identifier != "web":
# At the moment, it seems sufficient to allow this for the same channel types as other payment settings
# We can always introduce more flags later if needed
suffix = '_' + c.identifier.replace(".", "_")
self.term_channel_fields[c] = [
'payment_term_mode' + suffix,
'payment_term_days' + suffix,
'payment_term_minutes' + suffix,
]
self.fields['payment_term_mode' + suffix] = DEFAULTS['payment_term_mode']['form_class'](
label=_("Payment term"),
widget=forms.RadioSelect,
required=False,
choices=(
('', _("same as above")),
('days', _("different payment term in days")),
('minutes', _("different payment term in minutes"))
),
)
self.fields['payment_term_days' + suffix] = DEFAULTS['payment_term_days']['form_class'](
required=False,
**DEFAULTS['payment_term_days']['form_kwargs'](suffix, 1),
)
self.fields['payment_term_minutes' + suffix] = DEFAULTS['payment_term_minutes']['form_class'](
required=False,
**DEFAULTS['payment_term_minutes']['form_kwargs'](suffix, 2),
)
def clean(self):
data = super().clean()
for c in self.term_channel_fields.keys():
suffix = '_' + c.identifier.replace(".", "_")
mode = self.cleaned_data.get(f'payment_term_mode{suffix}')
if mode == 'days' and self.cleaned_data.get(f'payment_term_days{suffix}') is None:
raise ValidationError({f'payment_term_days{suffix}': _("This field is required.")})
if mode == 'minutes' and self.cleaned_data.get(f'payment_term_minutes{suffix}') is None:
raise ValidationError({f'payment_term_minutes{suffix}': _("This field is required.")})
return data
def clean_payment_term_days(self):
value = self.cleaned_data.get('payment_term_days')
if self.cleaned_data.get('payment_term_mode') == 'days' and value is None:
+3 -3
View File
@@ -105,12 +105,12 @@ class GlobalSettingsForm(SettingsForm):
domain=settings.SITE_URL
)
)),
('widget_vite_origins', forms.CharField(
('widget_vue2_origins', forms.CharField(
widget=forms.Textarea(attrs={'rows': '3'}),
required=False,
# Not translated on purpose, this is a temporary feature and contains too many special case words
label="Vite widget origins",
help_text="One origin per line (e.g. https://example.com). Requests from these origins will be served the new vite-based widget.",
label="Vue2 widget origins",
help_text="One origin per line (e.g. https://example.com). Requests from these origins will be served the old vue2-based widget.",
))
])
responses = register_global_settings.send(self)
+12 -3
View File
@@ -153,11 +153,19 @@ class QuestionForm(I18nModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['items'].queryset = self.instance.event.items.all()
self.fields['items'].required = True
if self.instance.container_type == Question.ContainerType.ORDERPOSITION:
self.fields['items'].queryset = self.instance.event.items.all()
self.fields['items'].required = True
else:
del self.fields['items']
del self.fields['ask_during_checkin']
del self.fields['show_during_checkin']
del self.fields['print_on_invoice']
self.fields['dependency_question'].widget.attrs['data-container-type'] = self.instance.container_type
self.fields['dependency_question'].queryset = self.instance.event.questions.filter(
type__in=(Question.TYPE_BOOLEAN, Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE),
ask_during_checkin=False
ask_during_checkin=False,
container_type=self.instance.container_type,
)
if self.instance.pk:
self.fields['dependency_question'].queryset = self.fields['dependency_question'].queryset.exclude(
@@ -243,6 +251,7 @@ class QuestionForm(I18nModelForm):
'valid_date_min',
'valid_date_max',
'valid_file_portrait',
'valid_string_length_min',
'valid_string_length_max',
]
widgets = {
+2
View File
@@ -87,6 +87,7 @@ class RRuleForm(forms.Form):
('1', pgettext_lazy('rrule', 'first')),
('2', pgettext_lazy('rrule', 'second')),
('3', pgettext_lazy('rrule', 'third')),
('4', pgettext_lazy('rrule', 'fourth')),
('-1', pgettext_lazy('rrule', 'last')),
],
required=False
@@ -134,6 +135,7 @@ class RRuleForm(forms.Form):
('1', pgettext_lazy('rrule', 'first')),
('2', pgettext_lazy('rrule', 'second')),
('3', pgettext_lazy('rrule', 'third')),
('4', pgettext_lazy('rrule', 'fourth')),
('-1', pgettext_lazy('rrule', 'last')),
],
required=False
+6 -6
View File
@@ -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):
+3
View File
@@ -861,6 +861,9 @@ class OrganizerPluginStateLogEntryType(LogEntryType):
'pretix.event.question.option.added': _('An answer option has been added to the question.'),
'pretix.event.question.option.deleted': _('An answer option has been removed from the question.'),
'pretix.event.question.option.changed': _('An answer option has been changed.'),
'pretix.event.questionnaire.added': _('A questionnaire has been created.'),
'pretix.event.questionnaire.deleted': _('A questionnaire has been deleted.'),
'pretix.event.questionnaire.changed': _('A questionnaire has been changed.'),
'pretix.event.permissions.added': _('A user has been added to the event team.'),
'pretix.event.permissions.invited': _('A user has been invited to the event team.'),
'pretix.event.permissions.changed': _('A user\'s permissions have been changed.'),
+3 -3
View File
@@ -182,12 +182,12 @@ def get_event_navigation(request: HttpRequest):
'active': 'event.items.categories' in url.url_name,
},
{
'label': _('Questions'),
'url': reverse('control:event.items.questions', kwargs={
'label': _('Questionnaires'),
'url': reverse('control:event.items.questionnaires', kwargs={
'event': request.event.slug,
'organizer': request.event.organizer.slug,
}),
'active': 'event.items.questions' in url.url_name,
'active': 'event.items.questionnaires' in url.url_name or 'event.items.questions' in url.url_name,
},
{
'label': _('Discounts'),
@@ -58,6 +58,7 @@
<script type="text/javascript" src="{% static "leaflet/leaflet.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/geo.js" %}"></script>
<script type="text/javascript" src="{% static "pretixbase/js/details.js" %}"></script>
<script type="text/javascript" src="{% static "pretixbase/js/reldate.js" %}"></script>
<script type="text/javascript" src="{% static "pretixbase/js/asynctask.js" %}"></script>
<script type="text/javascript" src="{% static "sortable/Sortable.js" %}"></script>
<script type="text/javascript" src="{% static "colorpicker/bootstrap-colorpicker.js" %}"></script>
@@ -2,9 +2,10 @@
{% load i18n %}
{% load static %}
{% load bootstrap3 %}
{% load getitem %}
{% block inside %}
<h1>{% trans "Payment settings" %}</h1>
<form action="" method="post" class="form-horizontal form-plugins">
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<div class="tabbed-form">
<fieldset>
@@ -71,20 +72,44 @@
{% bootstrap_form_errors form layout="control" %}
{% bootstrap_field form.payment_term_mode layout="control" %}
{% bootstrap_field form.payment_term_days layout="control" %}
{% bootstrap_field form.payment_term_weekdays layout="control" %}
{% bootstrap_field form.payment_term_minutes layout="control" %}
{% bootstrap_field form.payment_term_weekdays layout="control" %}
{% bootstrap_field form.payment_term_last layout="control" %}
{% bootstrap_field form.payment_term_expire_automatically layout="control" %}
{% trans "days" context "unit" as days %}
{% bootstrap_field form.payment_term_expire_delay_days layout="control" addon_after=days %}
{% bootstrap_field form.payment_term_accept_late layout="control" %}
{% bootstrap_field form.payment_pending_hidden layout="control" %}
{% for c, fields in form.term_channel_fields.items %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
{% if "." in c.icon %}
<img src="{% static c.icon %}" class="fa-like-image"
data-toggle="tooltip" title="{{ c.type_instance.verbose_name }}">
{% else %}
<span class="fa fa-fw fa-{{ c.icon }} text-muted"
data-toggle="tooltip" title="{{ c.type_instance.verbose_name }}"></span>
{% endif %}
{{ c.label }}
</h3>
</div>
<div class="panel-body">
{% for f in fields %}
{% bootstrap_field form|getitem:f layout="control" %}
{% endfor %}
</div>
</div>
{% endfor %}
</fieldset>
<fieldset>
<legend>{% trans "Advanced" %}</legend>
{% bootstrap_form_errors form layout="control" %}
{% bootstrap_field form.tax_rule_payment layout="control" %}
{% bootstrap_field form.payment_explanation layout="control" %}
{% bootstrap_field form.payment_choice_postpone_allowed_channels layout="control" %}
</fieldset>
</div>
{% if "event.settings.payment:write" in request.eventpermset %}
@@ -275,6 +275,9 @@
{% bootstrap_field sform.event_calendar_future_only layout="control" %}
{% endif %}
{% bootstrap_field sform.low_availability_percentage layout="control" addon_after="%" %}
{% if sform.event_list_empty_text %}
{% bootstrap_field sform.event_list_empty_text layout="control" %}
{% endif %}
<h4>{% trans "Order details" %}</h4>
@@ -13,9 +13,11 @@
{% blocktrans with url=edit_url|add:"#tab-0-1-open" %}If you want to keep the answers, <a href="{{url}}">edit the question</a> and set it to hidden.{% endblocktrans %}
</p>
</div>
{% for item in dependent %}
<li><a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.pk %}">{{ item }}</a></li>
{% endfor %}
<ul>
{% for item in dependent %}
<li><a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.pk %}">{{ item }}</a></li>
{% endfor %}
</ul>
{% endif %}
<div class="form-group submit-group">
<a href="{% url "control:event.items.questions" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default btn-cancel">
@@ -12,142 +12,120 @@
{% endblock %}
{% block inside %}
{% if question %}
<h1>{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}</h1>
<h1>{% blocktrans with name=question.question %}Data field: {{ name }}{% endblocktrans %}</h1>
{% else %}
<h1>{% trans "Question" %}</h1>
<h1>{% trans "Data field" %}</h1>
{% endif %}
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
{% bootstrap_form_errors form %}
<div class="tabbed-form">
<fieldset>
<legend>{% trans "General" %}</legend>
{% bootstrap_field form.question layout="control" %}
{% bootstrap_field form.type layout="control" %}
{% bootstrap_field form.items layout="control" %}
{% bootstrap_field form.required layout="control" %}
<div class="alert alert-info alert-required-boolean">
{% blocktrans trimmed %}
If you mark a Yes/No question as required, it means that the user has to select Yes and No is not
accepted. If you want to allow both options, do not make this field required.
{% endblocktrans %}
</div>
<div id="valid-number">
{% bootstrap_field form.valid_number_min layout="control" %}
{% bootstrap_field form.valid_number_max layout="control" %}
</div>
<div id="valid-date">
{% bootstrap_field form.valid_date_min layout="control" %}
{% bootstrap_field form.valid_date_max layout="control" %}
</div>
<div id="valid-datetime">
{% bootstrap_field form.valid_datetime_min layout="control" %}
{% bootstrap_field form.valid_datetime_max layout="control" %}
</div>
<div id="valid-string">
{% bootstrap_field form.valid_string_length_max layout="control" %}
</div>
<div id="valid-file">
{% bootstrap_field form.valid_file_portrait layout="control" %}
</div>
<div id="answer-options">
<h3>{% trans "Answer options" %}</h3>
<noscript>
<p>{% trans "Only applicable if you choose 'Choose one/multiple from a list' above." %}</p>
</noscript>
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}" data-formset-delete-confirm-text="{% trans "If you delete an answer option, you will no longer be able to see statistical data on customers who previously selected this option, and when such customers edit their answers, they need to select a different option." %}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="row question-option-row">
<div class="col-xs-10">
<span class="text-muted">
{% blocktrans trimmed with id=form.instance.identifier %}
Answer option {{ id }}
{% endblocktrans %}
</span>
{% bootstrap_form_errors form %}
{% bootstrap_field form.answer layout='inline' form_group_class="" %}
</div>
<div class="col-xs-2 text-right flip">
<span>&nbsp;</span><br>
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="row question-option-row">
<div class="col-xs-10">
<span class="text-muted">
{% trans "New answer option" %}
</span>
{% bootstrap_field formset.empty_form.answer layout='inline' form_group_class="" %}
</div>
<div class="col-xs-2 text-right flip">
<span>&nbsp;</span><br>
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new option" %}</button>
</p>
</div>
</div>
</fieldset>
<fieldset>
<legend>{% trans "Advanced" %}</legend>
{% bootstrap_field form.help_text layout="control" %}
{% bootstrap_field form.identifier layout="control" %}
{% bootstrap_field form.ask_during_checkin layout="control" %}
{% bootstrap_field form.show_during_checkin layout="control" %}
{% bootstrap_field form.hidden layout="control" %}
{% bootstrap_field form.print_on_invoice layout="control" %}
<div class="form-group">
<label class="col-md-3 control-label" for="id_dependency_question">
{% trans "Question dependency" %}
<br><span class="optional">{% trans "Optional" context "form" %}</span>
</label>
<div class="col-md-4">
{% bootstrap_field form.dependency_question layout="inline" form_group_class="inner" %}
</div>
<div class="col-md-5">
<script type="text/plain" id="dependency_value_val">{{ form.instance.dependency_values|escapejson_dumps }}</script>
{% bootstrap_field form.dependency_values layout="inline" form_group_class="inner" %}
</div>
</div>
</fieldset>
{% bootstrap_field form.question layout="control" %}
{% bootstrap_field form.type layout="control" %}
<div id="valid-number">
{% bootstrap_field form.valid_number_min layout="control" %}
{% bootstrap_field form.valid_number_max layout="control" %}
</div>
<div id="valid-date">
{% bootstrap_field form.valid_date_min layout="control" %}
{% bootstrap_field form.valid_date_max layout="control" %}
</div>
<div id="valid-datetime">
{% bootstrap_field form.valid_datetime_min layout="control" %}
{% bootstrap_field form.valid_datetime_max layout="control" %}
</div>
<div id="valid-string">
{% bootstrap_field form.valid_string_length_min layout="control" %}
{% bootstrap_field form.valid_string_length_max layout="control" %}
</div>
<div id="valid-file">
{% bootstrap_field form.valid_file_portrait layout="control" %}
</div>
<div id="answer-options">
<h3>{% trans "Answer options" %}</h3>
<noscript>
<p>{% trans "Only applicable if you choose 'Choose one/multiple from a list' above." %}</p>
</noscript>
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}" data-formset-delete-confirm-text="{% trans "If you delete an answer option, you will no longer be able to see statistical data on customers who previously selected this option, and when such customers edit their answers, they need to select a different option." %}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="row question-option-row">
<div class="col-xs-10">
<span class="text-muted">
{% blocktrans trimmed with id=form.instance.identifier %}
Answer option {{ id }}
{% endblocktrans %}
</span>
{% bootstrap_form_errors form %}
{% bootstrap_field form.answer layout='inline' form_group_class="" %}
</div>
<div class="col-xs-2 text-right flip">
<span>&nbsp;</span><br>
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="row question-option-row">
<div class="col-xs-10">
<span class="text-muted">
{% trans "New answer option" %}
</span>
{% bootstrap_field formset.empty_form.answer layout='inline' form_group_class="" %}
</div>
<div class="col-xs-2 text-right flip">
<span>&nbsp;</span><br>
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new option" %}</button>
</p>
</div>
</div>
{% bootstrap_field form.identifier layout="control" %}
{% if form.ask_during_checkin %}
{% bootstrap_field form.ask_during_checkin layout="control" %}
{% endif %}
{% if form.show_during_checkin %}
{% bootstrap_field form.show_during_checkin layout="control" %}
{% endif %}
{% bootstrap_field form.hidden layout="control" %}
{% if form.print_on_invoice %}
{% bootstrap_field form.print_on_invoice layout="control" %}
{% endif %}
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
@@ -0,0 +1,41 @@
{% extends "pretixcontrol/items/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load static %}
{% load icon %}
{% load compress %}
{% load vite %}
{% block title %}
{% trans "Questionnaires" %}
{% endblock %}
{% block inside %}
<h1>
{% trans "Questionnaires" %}
<a href="{% url "control:event.items.questions" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default pull-right">
{% icon "wrench" %} {% trans "Manage data fields" %}
</a>
</h1>
<p>
{% blocktrans trimmed %}
Questionaires allow your attendees to fill in additional data about their ticket. If you provide food, one
example might be to ask your users about dietary requirements.
{% endblocktrans %}
</p>
<p>
TODO(questionnaires) : add more specific explanation of the questionnaire concept.
</p>
{{ request.event.settings.locales|json_script:"event_locales" }}
{{ questionnaire_type_choices|json_script:"questionnaire_type_choices" }}
{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=0 as datafield_edit_url %}
{{ datafield_edit_url|json_script:"datafield_edit_url" }}
<div id="questionnaires-editor">
<!-- Vue app mount point -->
</div>
{% vite_hmr %}
{% vite_asset "src/pretix/static/pretixcontrol/js/ui/questionnaires/index.ts" %}
{% endblock %}
@@ -1,8 +1,8 @@
{% extends "pretixcontrol/items/base.html" %}
{% load i18n %}
{% block title %}{% trans "Questions" %}{% endblock %}
{% block title %}{% trans "Data fields" %}{% endblock %}
{% block inside %}
<h1>{% trans "Questions" %}</h1>
<h1>{% trans "Data fields" %}</h1>
<p>
{% blocktrans trimmed %}
Questions allow your attendees to fill in additional data about their ticket. If you provide food, one
@@ -10,49 +10,52 @@
{% endblocktrans %}
</p>
{% csrf_token %}
{% if 'event.items:write' in request.eventpermset %}
<p>
<a href="{% url "control:event.items.questions.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new question" %}
</a>
</p>
{% if request.event.settings.feature_flag_order_level_questions %}
{% if 'event.items:write' in request.eventpermset %}
<p>
<a href="{% url "control:event.items.questions.add" organizer=request.event.organizer.slug event=request.event.slug %}?container_type=P" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new per-ticket data field" %}
</a>
<a href="{% url "control:event.items.questions.add" organizer=request.event.organizer.slug event=request.event.slug %}?container_type=O" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new order-level data field" %}
</a>
</p>
{% endif %}
<h2>{% trans "Per-ticket data fields" %}</h2>
<p>{% trans "These data field can be used on individual tickets." %}</p>
{% else %}
{% if 'event.items:write' in request.eventpermset %}
<p>
<a href="{% url "control:event.items.questions.add" organizer=request.event.organizer.slug event=request.event.slug %}?container_type=P" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new data field" %}
</a>
</p>
{% endif %}
{% endif %}
<div class="table-responsive">
<table class="table table-hover table-quotas">
<thead>
<tr>
<th>{% trans "Question" %}</th>
<th>{% trans "Internal name" %}</th>
<th>{% trans "Type" %}</th>
<th class="iconcol"></th>
<th class="iconcol"></th>
<th class="iconcol"></th>
<th>{% trans "Products" %}</th>
{% if 'event.items:write' in request.eventpermset %}
<th class="action-col-2"></th>
{% endif %}
<th class="action-col-2"></th>
</tr>
</thead>
<tbody data-dnd-url="{% url "control:event.items.questions.reorder" organizer=request.event.organizer.slug event=request.event.slug %}">
{% for q in questions %}
<tr data-dnd-id="{{ q.id }}">
<tbody>
{% for q in questions %}{% if q.container_type == "P" %}
<tr>
<td>
<strong>
{% if q.pk %}
<a href="{% url "control:event.items.questions.show" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}">
{% endif %}
{{ q.question }}
{% if q.pk %}
</a>
{% endif %}
<a href="{% url "control:event.items.questions.show" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}">
{{ q.question }}
</a>
</strong><br>
<small class="text-muted">{{ q.identifier }}</small>
</td>
<td>
{% if q.pk %}
{{ q.get_type_display }}
{% else %}
{% trans "System question" %}
{% endif %}
{{ q.get_type_display }}
</td>
<td>
{% if q.required %}
@@ -63,47 +66,93 @@
{% if q.pk and q.ask_during_checkin %}
<span class="fa fa-check-square text-muted" data-toggle="tooltip" title="{% trans "Ask during check-in" %}"></span>
{% endif %}
</td>
<td>
{% if q.pk and q.hidden %}
<span class="fa fa-eye-slash text-muted" data-toggle="tooltip" title="{% trans "Hidden question" %}"></span>
{% endif %}
</td>
<td>
{% if q.pk %}
<ul>
{% for item in q.items.all %}
<li>
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.id %}">{{ item }}</a>
</li>
{% endfor %}
</ul>
{% else %}
<small>{% trans "All personalized products" %}</small>
{% endif %}
</td>
{% if 'event.items:write' in request.eventpermset %}
<td class="dnd-container">
</td>
{% endif %}
<td class="text-right flip">
{% if q.pk %}
<a href="{% url "control:event.items.questions.show" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-bar-chart"></i></a>
{% if 'event.items:write' in request.eventpermset %}
<a href="{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
<a href="{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
{% endif %}
{% else %}
{% if 'event.settings.general:write' in request.eventpermset %}
<a href="{% url "control:event.settings" organizer=request.event.organizer.slug event=request.event.slug %}#tab-0-2-open"
class="btn btn-default btn-sm"><i class="fa fa-wrench"></i></a>
{% endif %}
<a href="{% url "control:event.items.questions.show" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-bar-chart"></i></a>
{% if 'event.items:write' in request.eventpermset %}
<a href="{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
<a href="{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
{% endif %}
</td>
</tr>
{% endfor %}
{% endif %}{% endfor %}
</tbody>
</table>
</div>
{% if request.event.settings.feature_flag_order_level_questions %}
<h2>
{% trans "Per-order data fields" %}
<small><span class="label label-info" title="
{% trans "This functionality is in active development and expected to change significantly over the coming months." %}
{% trans "In pretixPOS, per-order data fields are currently not supported and will not be displayed." %}
" data-toggle="tooltip">
<span class="fa fa-flask" aria-hidden="true"></span>
{% trans "Experimental feature" %}
</span></small>
</h2>
<p>{% trans "These data fields are asked once per order." %}</p>
<div class="table-responsive">
<table class="table table-hover table-quotas">
<thead>
<tr>
<th>{% trans "Internal name" %}</th>
<th>{% trans "Type" %}</th>
<th class="iconcol"></th>
<th class="iconcol"></th>
<th class="iconcol"></th>
<th class="action-col-2"></th>
</tr>
</thead>
<tbody>
{% for q in questions %}{% if q.container_type == "O" %}
<tr>
<td>
<strong>
{{ q.question }}
</strong><br>
<small class="text-muted">{{ q.identifier }}</small>
</td>
<td>
{{ q.get_type_display }}
</td>
<td>
{% if q.required %}
<span class="fa fa-exclamation-circle text-muted" data-toggle="tooltip" title="{% trans "Required question" %}"></span>
{% endif %}
</td>
<td>
{% if q.pk and q.ask_during_checkin %}
<span class="fa fa-check-square text-muted" data-toggle="tooltip" title="{% trans "Ask during check-in" %}"></span>
{% endif %}
</td>
<td>
{% if q.pk and q.hidden %}
<span class="fa fa-eye-slash text-muted" data-toggle="tooltip" title="{% trans "Hidden question" %}"></span>
{% endif %}
</td>
<td class="text-right flip">
{% if q.pk %}
{% if 'event.items:write' in request.eventpermset %}
<a href="{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
<a href="{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
{% endif %}
{% else %}
{% if 'event.settings.general:write' in request.eventpermset %}
<a href="{% url "control:event.settings" organizer=request.event.organizer.slug event=request.event.slug %}#tab-0-2-open"
class="btn btn-default btn-sm"><i class="fa fa-wrench"></i></a>
{% endif %}
{% endif %}
</td>
</tr>
{% endif %}{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{% endblock %}
@@ -35,6 +35,18 @@
</div>
</details>
{% endif %}
{% if order_questions_form.fields %}
<details class="panel panel-default" open>
<summary class="panel-heading">
<h4 class="panel-title">
<strong>{% trans "Additional order information" %}</strong>
</h4>
</summary>
<div class="panel-body">
{% bootstrap_form order_questions_form layout="horizontal" %}
</div>
</details>
{% endif %}
{% for pos, forms in formgroups %}
<details class="panel panel-default" open>
<summary class="panel-heading">
@@ -0,0 +1,30 @@
{% load safelink %}
{% load i18n %}
{% load rich_text %}
{% if answer %}
{% if answer.file %}
<span class="fa fa-file"></span>
<a href="{{ answer.backend_file_url }}?token={% answer_token request answer %}">
{{ answer.file_name }}
</a>
<span class="label label-danger" data-toggle="tooltip"
title="{% trans "This file has been uploaded by a user and could contain viruses or other malicious content." %}">
{% trans "UNSAFE" %}
</span>
{% if answer.is_image %}
<br>
<a href="{{ answer.backend_file_url }}?token={% answer_token request answer %}" data-lightbox="order"
class="answer-thumb">
<img src="{{ answer.backend_file_url }}?token={% answer_token request answer %}">
</a>
{% endif %}
{% elif question.type == "M" %}
{{ answer.to_string_i18n|rich_text_snippet }}
{% elif question.type %}
{{ answer.to_string_i18n|rich_text_snippet }}
{% else %}
{{ answer|linebreaksbr }}
{% endif %}
{% else %}
<em>{% trans "not answered" %}</em>
{% endif %}
@@ -427,6 +427,14 @@
</form>
</dd>
{% endif %}
{% if order.answers.all %}
<hr>
{% endif %}
{% for ans in order.answers.all %}
<dt>{{ ans.question.internal_name|default:ans.question.question }}</dt>
<dd>{% include "pretixcontrol/order/fragment_question_answer.html" with request=request question=ans.question answer=ans %}</dd>
{% endfor %}
</dl>
</div>
</div>
@@ -602,60 +610,9 @@
{% endif %}
{% if line.has_questions %}
<dl>
{% if line.item.ask_attendee_data and event.settings.attendee_names_asked %}
<dt>{% trans "Attendee name" %}</dt>
<dd>{% if line.attendee_name %}{{ line.attendee_name_all_components }}{% else %}
<em>{% trans "not answered" %}</em>{% endif %}</dd>
{% endif %}
{% if line.item.ask_attendee_data and event.settings.attendee_emails_asked %}
<dt>{% trans "Attendee email" %}</dt>
<dd>
{% if line.attendee_email %}
{{ line.attendee_email }}
{% if not line.addon_to %}
<form class="form-inline helper-display-inline" method="post"
action="{% url "control:event.order.resendlink" event=request.event.slug organizer=request.event.organizer.slug code=order.code position=line.pk %}">
{% csrf_token %}
<a href="{% url "control:event.order.position.sendmail" event=request.event.slug organizer=request.event.organizer.slug code=order.code position=line.pk %}"
class="btn btn-default btn-xs">
<span class="fa fa-envelope-o"></span>
</a>
<button class="btn btn-default btn-xs">
{% trans "Resend link" %}
</button>
</form>
{% endif %}
{% else %}
<em>{% trans "not answered" %}</em>
{% endif %}
</dd>
{% endif %}
{% if line.item.ask_attendee_data and event.settings.attendee_company_asked %}
<dt>
{% trans "Attendee company" %}
</dt>
<dd>
{% if line.company %}{{ line.company }}{% else %}<em>{% trans "not answered" %}</em>{% endif %}
</dd>
{% endif %}
{% if line.item.ask_attendee_data and event.settings.attendee_addresses_asked %}
<dt>
{% trans "Attendee address" %}
</dt>
<dd>
{% if line.street or line.zipcode or line.city or line.country %}
{{ line.street|default_if_none:""|linebreaksbr }}<br>
{{ line.zipcode|default_if_none:"" }} {{ line.city|default_if_none:"" }}<br>
{% if line.state %}{{ line.state_for_address }}<br>{% endif %}
{{ line.country.name|default_if_none:"" }}
{% else %}
<em>{% trans "not answered" %}</em>
{% endif %}
</dd>
{% endif %}
{% for q in line.questions %}
<dt>
{{ q.question }}
{{ q.label }}
{% if q.ask_during_checkin %}
<span class="fa fa-qrcode text-muted"
data-toggle="tooltip"
@@ -664,6 +621,7 @@
{% endif %}
</dt>
<dd>
{#
{% if q.answer %}
{% if q.answer.file %}
<span class="fa fa-file"></span>
@@ -683,12 +641,29 @@
{% endif %}
{% elif q.type == "M" %}
{{ q.answer.to_string_i18n|rich_text_snippet }}
{% else %}
{% elif q.type %}
{{ q.answer.to_string_i18n|linebreaksbr }}
{% else %}<!-- TODO(questionnaires): proper separation of QuestionAnswer objects and system answers...... -->
{{ q.answer|linebreaksbr }}
{% endif %}
{% else %}
<em>{% trans "not answered" %}</em>
{% endif %}
#}
{% include "pretixcontrol/order/fragment_question_answer.html" with request=request question=q answer=q.answer %}
{% if q.system_datafield == "attendee_email" and not line.addon_to %}
<form class="form-inline helper-display-inline" method="post"
action="{% url "control:event.order.resendlink" event=request.event.slug organizer=request.event.organizer.slug code=order.code position=line.pk %}">
{% csrf_token %}
<a href="{% url "control:event.order.position.sendmail" event=request.event.slug organizer=request.event.organizer.slug code=order.code position=line.pk %}"
class="btn btn-default btn-xs">
<span class="fa fa-envelope-o"></span>
</a>
<button class="btn btn-default btn-xs">
{% trans "Resend link" %}
</button>
</form>
{% endif %}
</dd>
{% endfor %}
{% for q in line.additional_fields %}
@@ -118,6 +118,10 @@
</td>
{% endif %}
<td>
<span class="fa fa-fw {% if d.last_seen and d.last_seen.is_recent %}fa-check-circle text-success{% else %}fa-circle text-danger{% endif %}"
data-toggle="tooltip"
title="{% if d.last_seen %}{% blocktrans with time=d.last_seen.last_seen|date:"SHORT_DATETIME_FORMAT" %}Last seen: {{ time }}{% endblocktrans %}{% else %}{% trans "No recent contact" %}{% endif %}"
></span>
{{ d.device_id }}
</td>
<td>
@@ -125,6 +129,7 @@
<del>{% endif %}
{{ d.name }}
{% if d.revoked %}</del>{% endif %}
{% if d.gate %}
<br>
<small class="text-muted">{{ d.gate.name }}</small>
+1
View File
@@ -350,6 +350,7 @@ urlpatterns = [
re_path(r'^questions/(?P<question>\d+)/change$', item.QuestionUpdate.as_view(),
name='event.items.questions.edit'),
re_path(r'^questions/add$', item.QuestionCreate.as_view(), name='event.items.questions.add'),
re_path(r'^questionnaires/$', item.QuestionnairesEditor.as_view(), name='event.items.questionnaires'),
re_path(r'^quotas/$', item.QuotaList.as_view(), name='event.items.quotas'),
re_path(r'^quotas/bulk_action$', item.QuotaBulkAction.as_view(), name='event.items.quotas.bulkaction'),
re_path(r'^quotas/bulk_edit$', item.QuotaBulkUpdateView.as_view(), name='event.items.quotas.bulkedit'),
+12 -9
View File
@@ -64,6 +64,7 @@ from pretix.base.forms.auth import (
)
from pretix.base.metrics import pretix_failed_logins, pretix_successful_logins
from pretix.base.models import TeamInvite, U2FDevice, User, WebAuthnDevice
from pretix.helpers import OF_SELF
from pretix.helpers.http import get_client_ip, redirect_to_url
from pretix.helpers.ratelimit import rate_limit, rate_limit_reset
from pretix.helpers.security import handle_login_source, session_login
@@ -395,15 +396,17 @@ class Recover(TemplateView):
def post(self, request, *args, **kwargs):
if self.form.is_valid():
try:
user = User.objects.get(id=self.request.GET.get('id'), auth_backend='native')
except User.DoesNotExist:
return self.invalid('unknownuser')
if not default_token_generator.check_token(user, self.request.GET.get('token')):
return self.invalid('invalid')
user.set_password(self.form.cleaned_data['password'])
user.needs_password_change = False
user.save()
with transaction.atomic():
# Check token in transaction to prevent race condition
try:
user = User.objects.select_for_update(of=OF_SELF).get(id=self.request.GET.get('id'), auth_backend='native')
except User.DoesNotExist:
return self.invalid('unknownuser')
if not default_token_generator.check_token(user, self.request.GET.get('token')):
return self.invalid('invalid')
user.set_password(self.form.cleaned_data['password'])
user.needs_password_change = False
user.save()
messages.success(request, _('You can now login using your new password.'))
user.log_action('pretix.control.auth.user.forgot_password.recovered')
+3
View File
@@ -163,6 +163,9 @@ class DiscountCreate(EventPermissionRequiredMixin, CreateView):
i = modelcopy(self.copy_from)
i.pk = None
kwargs['instance'] = i
kwargs["initial"]["limit_sales_channels"] = self.copy_from.limit_sales_channels.all()
kwargs["initial"]["condition_limit_products"] = self.copy_from.condition_limit_products.all()
kwargs["initial"]["benefit_limit_products"] = self.copy_from.benefit_limit_products.all()
else:
kwargs['instance'] = Discount(event=self.request.event)
+11 -136
View File
@@ -56,7 +56,7 @@ from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import gettext, gettext_lazy as _
from django.views.decorators.http import require_http_methods
from django.views.generic import FormView, ListView, View
from django.views.generic import FormView, ListView, TemplateView, View
from django.views.generic.detail import DetailView, SingleObjectMixin
from django_countries.fields import Country
@@ -426,7 +426,7 @@ def reorder_categories(request, organizer, event):
FakeQuestion = namedtuple(
'FakeQuestion', 'id question position required'
'FakeQuestion', 'id question position required container_type'
)
@@ -436,140 +436,7 @@ class QuestionList(ListView):
template_name = 'pretixcontrol/items/questions.html'
def get_queryset(self):
return self.request.event.questions.prefetch_related('items')
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
questions = []
if self.request.event.settings.attendee_names_asked:
questions.append(
FakeQuestion(
id='attendee_name_parts',
question=_('Attendee name'),
position=self.request.event.settings.system_question_order.get(
'attendee_name_parts', 0
),
required=self.request.event.settings.attendee_names_required,
)
)
if self.request.event.settings.attendee_emails_asked:
questions.append(
FakeQuestion(
id='attendee_email',
question=_('Attendee email'),
position=self.request.event.settings.system_question_order.get(
'attendee_email', 0
),
required=self.request.event.settings.attendee_emails_required,
)
)
if self.request.event.settings.attendee_company_asked:
questions.append(
FakeQuestion(
id='company',
question=_('Company'),
position=self.request.event.settings.system_question_order.get(
'company', 0
),
required=self.request.event.settings.attendee_company_required,
)
)
if self.request.event.settings.attendee_addresses_asked:
questions.append(
FakeQuestion(
id='street',
question=_('Street'),
position=self.request.event.settings.system_question_order.get(
'street', 0
),
required=self.request.event.settings.attendee_addresses_required,
)
)
questions.append(
FakeQuestion(
id='zipcode',
question=_('ZIP code'),
position=self.request.event.settings.system_question_order.get(
'zipcode', 0
),
required=self.request.event.settings.attendee_addresses_required,
)
)
questions.append(
FakeQuestion(
id='city',
question=_('City'),
position=self.request.event.settings.system_question_order.get(
'city', 0
),
required=self.request.event.settings.attendee_addresses_required,
)
)
questions.append(
FakeQuestion(
id='country',
question=_('Country'),
position=self.request.event.settings.system_question_order.get(
'country', 0
),
required=self.request.event.settings.attendee_addresses_required,
)
)
questions += list(ctx['questions'])
questions.sort(key=lambda q: q.position)
ctx['questions'] = questions
return ctx
@transaction.atomic
@event_permission_required("event.items:write")
@require_http_methods(["POST"])
def reorder_questions(request, organizer, event):
try:
ids = json.loads(request.body.decode('utf-8'))['ids']
except (JSONDecodeError, KeyError, ValueError):
return HttpResponseBadRequest("expected JSON: {ids:[]}")
# filter system_questions - normal questions are int/digit, system_questions strings
custom_question_ids = [i for i in ids if i.isdigit()]
input_questions = list(request.event.questions.filter(id__in=custom_question_ids))
if len(input_questions) != len(custom_question_ids):
raise Http404(_("Some of the provided object ids are invalid."))
if len(input_questions) != request.event.questions.count():
raise Http404(_("Not all objects have been selected."))
for q in input_questions:
pos = ids.index(str(q.pk))
if pos != q.position: # Save unneccessary UPDATE queries
q.position = pos
q.save(update_fields=['position'])
q.log_action(
'pretix.event.question.reordered', user=request.user, data={
'position': pos,
}
)
system_question_order = {}
for s in ('attendee_name_parts', 'attendee_email', 'company', 'street', 'zipcode', 'city', 'country'):
if s in ids:
system_question_order[s] = ids.index(s)
else:
system_question_order[s] = -1
request.event.settings.system_question_order = system_question_order
request.event.log_action(
'pretix.event.settings', user=request.user, data={
'system_question_order': system_question_order,
}
)
return HttpResponse()
return self.request.event.questions
class QuestionDelete(EventPermissionRequiredMixin, CompatDeleteView):
@@ -805,6 +672,9 @@ class QuestionCreate(EventPermissionRequiredMixin, QuestionMixin, CreateView):
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['instance'] = Question(event=self.request.event)
kwargs['instance'].container_type = self.request.GET.get('container_type', Question.ContainerType.ORDERPOSITION)
if kwargs['instance'].container_type not in Question.ContainerType.values:
raise PermissionDenied
return kwargs
def get_success_url(self) -> str:
@@ -836,6 +706,11 @@ class QuestionCreate(EventPermissionRequiredMixin, QuestionMixin, CreateView):
return ret
class QuestionnairesEditor(EventPermissionRequiredMixin, TemplateView):
permission = 'can_change_items'
template_name = 'pretixcontrol/items/questionnaires.html'
class QuotaQueryMixin:
@cached_property
+16 -4
View File
@@ -1646,7 +1646,8 @@ class OrderCheckVATID(OrderView):
return redirect(self.get_order_url())
try:
normalized_id = validate_vat_id(ia.vat_id, str(ia.country))
requester_id = self.request.event.settings.invoice_address_from_vat_id
normalized_id = validate_vat_id(ia.vat_id, str(ia.country), requester_id)
with transaction.atomic():
ia.vat_id_validated = True
ia.vat_id = normalized_id
@@ -2008,7 +2009,10 @@ class OrderChange(OrderView):
'issued_gift_cards',
'addons',
).annotate(
_seat_allowed=Exists(SeatCategoryMapping.objects.filter(subevent=OuterRef("subevent"), product=OuterRef("item")))
_seat_allowed=Exists(SeatCategoryMapping.objects.filter(
subevent=OuterRef("subevent") if self.request.event.has_subevents else None,
product=OuterRef("item"))
)
))
for p in positions:
p.form = OrderPositionChangeForm(
@@ -2226,6 +2230,10 @@ class OrderModifyInformation(OrderQuestionsViewMixin, OrderView):
only_user_visible = False
all_optional = True
@property
def order_question_container(self):
return self.order
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['other_form'] = self.other_form
@@ -2587,7 +2595,11 @@ class AnswerDownload(EventPermissionRequiredMixin, OrderViewMixin, ListView):
answid = kwargs.get('answer')
token = request.GET.get('token', '')
answer = get_object_or_404(QuestionAnswer, orderposition__order=self.order, id=answid)
answer = get_object_or_404(
QuestionAnswer,
Q(orderposition__order=self.order) | Q(order=self.order),
id=answid
)
if not answer.file:
raise Http404()
if not check_token(request, answer, token):
@@ -2597,7 +2609,7 @@ class AnswerDownload(EventPermissionRequiredMixin, OrderViewMixin, ListView):
resp = FileResponse(answer.file, content_type=ftype or 'application/binary')
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}"'.format(
self.request.event.slug.upper(), self.order.code,
answer.orderposition.positionid,
answer.orderposition.positionid if answer.orderposition else '',
os.path.basename(answer.file.name).split('.', 1)[1]
)
return resp
+4 -2
View File
@@ -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(
+5
View File
@@ -27,6 +27,7 @@ from decimal import Decimal
from io import BytesIO
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.core.files import File
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
@@ -193,6 +194,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
c.expires = now() + timedelta(days=7)
c.date = now()
c.filename = 'background_preview.pdf'
c.bind_to_session(request, "ticketoutput-pdf-background")
c.type = 'application/pdf'
c.save()
c.file.save('empty.pdf', ContentFile(buffer.read()))
@@ -218,6 +220,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
c.expires = now() + timedelta(days=7)
c.date = now()
c.filename = 'background_preview.pdf'
c.bind_to_session(request, "ticketoutput-pdf-background")
c.type = 'application/pdf'
c.file = fileobj
c.save()
@@ -303,5 +306,7 @@ class FontsCSSView(TemplateView):
class PdfView(TemplateView):
def get(self, request, *args, **kwargs):
cf = get_object_or_404(CachedFile, id=kwargs.get("filename"), filename="background_preview.pdf")
if not cf.allowed_for_session(request, "ticketoutput-pdf-background"):
raise PermissionDenied()
resp = FileResponse(cf.file, filename=cf.filename, content_type='application/pdf')
return resp
+1 -1
View File
@@ -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,
+4 -2
View File
@@ -144,7 +144,7 @@ class VoucherList(VoucherQueryMixin, PaginationMixin, EventPermissionRequiredMix
headers = [
_('Voucher code'), _('Valid until'), _('Product'), _('Reserve quota'), _('Bypass quota'),
_('Price effect'), _('Value'), _('Tag'), _('Redeemed'), _('Maximum usages'), _('Seat'),
_('Comment')
_('Comment'), _('Budget'), _('Budget used')
]
writer.writerow(headers)
@@ -170,7 +170,9 @@ class VoucherList(VoucherQueryMixin, PaginationMixin, EventPermissionRequiredMix
str(v.redeemed),
str(v.max_usages),
str(v.seat) if v.seat else "",
str(v.comment) if v.comment else ""
str(v.comment) if v.comment else "",
str(v.budget) if v.budget is not None else "",
str(v.budget_used) if v.budget is not None else "",
]
writer.writerow(row)
+9
View File
@@ -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!
+6 -2
View File
@@ -24,17 +24,21 @@ import hashlib
from django.core.signing import BadSignature, TimestampSigner
class SafeDownloadSigner(TimestampSigner):
pass
def get_token(request, answer):
if not request.session.session_key:
request.session.create()
payload = '{}:{}'.format(request.session.session_key, answer.pk)
signer = TimestampSigner()
signer = SafeDownloadSigner()
return signer.sign(hashlib.sha1(payload.encode()).hexdigest())
def check_token(request, answer, token):
payload = hashlib.sha1('{}:{}'.format(request.session.session_key, answer.pk).encode()).hexdigest()
signer = TimestampSigner()
signer = SafeDownloadSigner()
try:
return payload == signer.unsign(token, max_age=3600 * 24)
except BadSignature:
+1 -1
View File
@@ -29,7 +29,7 @@ from django.urls import reverse
def build_absolute_uri(urlname, args=None, kwargs=None):
warnings.warn(
'Usage of build_absolute_uri is confusing since there are many functions with that name. '
'Replace this usage with ',
'Replace this usage with mainreverse_absolute.',
DeprecationWarning
)
return mainreverse_absolute(urlname, args, kwargs)
+210 -53
View File
@@ -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 15:35+0000\n"
"PO-Revision-Date: 2026-07-15 14:20+0000\n"
"Last-Translator: Llewelyn Barkhuizen <llewelyn@akademia.ac.za>\n"
"Language-Team: Afrikaans <https://translate.pretix.eu/projects/pretix/pretix/"
@@ -91,6 +91,10 @@ msgstr ""
msgid "Hebrew"
msgstr ""
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr ""
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr ""
@@ -410,6 +414,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 ""
@@ -838,8 +846,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
@@ -856,7 +863,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 ""
@@ -1587,9 +1593,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
@@ -1601,8 +1606,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 ""
@@ -1610,8 +1614,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 ""
@@ -1634,8 +1637,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"
@@ -1663,8 +1665,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"
@@ -2451,7 +2452,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
@@ -2956,13 +2958,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
@@ -2995,6 +2995,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 "
@@ -5321,6 +5331,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 ""
@@ -5358,6 +5372,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 "
@@ -5957,10 +5975,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 ""
@@ -6869,7 +6883,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
@@ -7596,6 +7610,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 ""
@@ -7620,6 +7642,26 @@ 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr ""
@@ -8705,7 +8747,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 ""
@@ -9286,6 +9328,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 ""
@@ -12427,6 +12488,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 ""
@@ -18577,6 +18654,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 ""
@@ -20228,6 +20306,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 ""
@@ -20244,6 +20340,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
@@ -20766,6 +20882,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 ""
@@ -20793,6 +20916,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"
@@ -21048,25 +21188,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
@@ -22304,6 +22429,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 ""
@@ -25179,10 +25313,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 ""
@@ -25328,7 +25458,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 ""
@@ -25718,7 +25848,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."
@@ -27760,6 +27891,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 the original 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 ""
@@ -27985,8 +28139,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
@@ -30719,10 +30876,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 ""
@@ -31120,6 +31273,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"
+210 -53
View File
@@ -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 15:35+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -89,6 +89,10 @@ msgstr ""
msgid "Hebrew"
msgstr ""
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr ""
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr ""
@@ -408,6 +412,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 ""
@@ -836,8 +844,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
@@ -854,7 +861,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 ""
@@ -1585,9 +1591,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
@@ -1599,8 +1604,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 ""
@@ -1608,8 +1612,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 ""
@@ -1632,8 +1635,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"
@@ -1661,8 +1663,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"
@@ -2449,7 +2450,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
@@ -2954,13 +2956,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
@@ -2993,6 +2993,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 "
@@ -5319,6 +5329,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 ""
@@ -5356,6 +5370,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 "
@@ -5955,10 +5973,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 ""
@@ -6867,7 +6881,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
@@ -7594,6 +7608,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 ""
@@ -7618,6 +7640,26 @@ 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr ""
@@ -8703,7 +8745,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 ""
@@ -9284,6 +9326,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 ""
@@ -12425,6 +12486,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 ""
@@ -18575,6 +18652,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 ""
@@ -20226,6 +20304,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 ""
@@ -20242,6 +20338,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
@@ -20764,6 +20880,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 ""
@@ -20791,6 +20914,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"
@@ -21046,25 +21186,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
@@ -22302,6 +22427,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 ""
@@ -25177,10 +25311,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 ""
@@ -25326,7 +25456,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 ""
@@ -25716,7 +25846,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."
@@ -27758,6 +27889,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 the original 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 ""
@@ -27983,8 +28137,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
@@ -30717,10 +30874,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 ""
@@ -31118,6 +31271,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"
+251 -65
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-30 13:52+0000\n"
"POT-Creation-Date: 2026-08-24 15:35+0000\n"
"PO-Revision-Date: 2026-07-20 08:01+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix/ar/"
@@ -92,6 +92,10 @@ msgstr "اليونانية"
msgid "Hebrew"
msgstr "العبرية"
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr ""
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr "الأندونيسية"
@@ -421,6 +425,12 @@ msgstr "الوسيط متصل بحدث آخر"
msgid "You cannot exchange a medium for a medium."
msgstr "لا يمكنك استبدال هذا الطلب (مقاس متوسط بمقاس متوسط)."
#: pretix/api/views/checkin.py
#, fuzzy
#| msgid "Product does not support medium exchange."
msgid "You cannot simulate a medium exchange."
msgstr "هذا المنتج لا يدعم استبدال الحجم المتوسط."
#: pretix/api/views/oauth.py pretix/control/logdisplay.py
#, python-brace-format
msgid ""
@@ -866,8 +876,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
@@ -884,7 +893,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 "البريد الإلكتروني للمشارك"
@@ -1635,9 +1643,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
@@ -1649,8 +1656,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 "الرمز البريدي"
@@ -1658,8 +1664,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 "المدينة"
@@ -1682,8 +1687,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"
@@ -1711,8 +1715,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"
@@ -2521,7 +2524,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
@@ -3039,14 +3043,12 @@ msgstr ""
"قم بتحميل صورة صالحة. الملف الذي قمت بتحميله ليس صورة أو هو صورة تالفة."
#: pretix/base/forms/questions.py
msgid ""
"If you keep this empty, the ticket will be valid starting at the time of "
"purchase."
msgstr "إذا تركت هذا الحقل فارغًا، فستكون التذكرة صالحة اعتبارًا من وقت الشراء."
msgid "Street"
msgstr "الشارع"
#: pretix/base/forms/questions.py
msgid "Street and Number"
msgstr "الشارع والرقم"
msgid "State"
msgstr "ولاية"
#: pretix/base/forms/questions.py
#, python-brace-format
@@ -3078,6 +3080,16 @@ msgstr "يرجى إدخال تاريخ ووقت لا يسبقان {min}."
msgid "Please enter a date and time no later than {max}."
msgstr "يرجى إدخال تاريخ ووقت لا يتجاوزان {max}."
#: 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 "
@@ -5620,6 +5632,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 "رقم"
@@ -5657,6 +5673,10 @@ msgstr "التاريخ والوقت"
msgid "Country code (ISO 3166-1 alpha-2)"
msgstr "رمز البلد (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 "
@@ -6299,10 +6319,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 "معرف عربة التسوق (مثل مفتاح الجلسة)"
@@ -7297,8 +7313,11 @@ msgid "The payment for this invoice has already been received."
msgstr "تم استلام دفعة هذه الفاتورة سابقا."
#: 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 "يجري حالياً معالجة عملية الدفع هذه، ولا يمكن إلغاؤها في الوقت الحالي."
#: pretix/base/payment.py
@@ -8060,6 +8079,18 @@ msgstr "بداية إعلان ما قبل البيع"
msgid "Presale end"
msgstr "نهاية إعلان ما قبل البيع"
#: pretix/base/reldate.py
#, fuzzy
#| msgid "Order email"
msgid "Order creation"
msgstr "البريد الإلكتروني الخاص بالطلب"
#: pretix/base/reldate.py
#, fuzzy
#| msgid "Order expired"
msgid "Order expiry"
msgstr "الطلب منتهي الصلاحية"
#: pretix/base/reldate.py
msgid "before"
msgstr "قبل"
@@ -8084,6 +8115,26 @@ 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr "عشوائي (افتراضي، يعمل مع جميع تطبيقات pretix)"
@@ -9446,7 +9497,7 @@ msgstr "تم خصم المبلغ من بطاقتك."
msgid "Please transfer money to this bank account: 9999-9999-9999-9999"
msgstr "يرجى تحويل المبلغ إلى هذا الحساب المصرفي: 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 "سيتم استبدال هذه القيمة بناءً على معلمات ديناميكية."
@@ -10142,6 +10193,25 @@ 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 "نص للتوجيه"
@@ -13966,6 +14036,30 @@ msgstr ""
"لقد قمت بتعيين صلاحية بطاقات الهدايا لتكون صالحة لمدة {} سنوات بالإضافة إلى "
"السنة التي تم إصدار البطاقة فيها."
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment date"
msgid "Payment term"
msgstr "يوم الدفع او الاستحقاق"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "(Same as above)"
msgid "same as above"
msgstr "(كما ورد أعلاه)"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment term in days"
msgid "different payment term in days"
msgstr "مدة السداد بالأيام"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment term in minutes"
msgid "different payment term in minutes"
msgstr "مدة السداد بالدقائق"
#: pretix/control/forms/event.py
msgid "Prices including tax"
msgstr "الأسعار شاملة الضريبة"
@@ -20716,6 +20810,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 "ميزة تجريبية"
@@ -22576,6 +22671,30 @@ msgstr ""
"أسئلة تسمح الحضور لملء بيانات إضافية عن تذاكرهم. إذا كنت توفير الغذاء، قد "
"يكون مثال واحد أن يسأل المستخدمين حول المتطلبات الغذائية."
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Create a new question"
msgid "Create a new per-ticket question"
msgstr "إنشاء سؤال جديد"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Create a new question"
msgid "Create a new order-level question"
msgstr "إنشاء سؤال جديد"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Delete question"
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 "إنشاء سؤال جديد"
@@ -22592,6 +22711,30 @@ msgstr "طرح خلال التسجيل في مكتب الاستقبال"
msgid "All personalized products"
msgstr "جميع المنتجات المخصصة"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Include questions"
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
#, fuzzy
#| msgid "The question has been reordered."
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
@@ -23178,6 +23321,15 @@ 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
#, fuzzy
#| msgid "Additional information"
msgid "Additional order information"
msgstr "معلومة اضافية"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid "Delete order"
msgstr "أجل حذف"
@@ -23207,6 +23359,25 @@ 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 "UNSAFE"
#: 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"
@@ -23479,27 +23650,10 @@ msgstr "%(datetime_range)s صالح"
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 "UNSAFE"
#: pretix/control/templates/pretixcontrol/order/index.html
#: pretix/control/templates/pretixcontrol/orders/overview.html
#: pretix/plugins/reports/accountingreport.py
@@ -24852,6 +25006,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 "البداية: %(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 "لم يتم تهيئة بعد"
@@ -28045,10 +28209,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 "عدم وجود مسألة المطلوبة."
@@ -28265,7 +28425,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 ""
@@ -28706,7 +28866,8 @@ msgstr "الموضوع: {subject}"
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."
@@ -30934,6 +31095,29 @@ msgstr ""
"امنح عملاءك إمكانية الشراء الآن (حتى حد معين) والدفع على أقساط متعددة أو في "
"غضون 30 يومًا. وأنت، بصفتك التاجر، ستحصل على أموالك على الفور."
#: 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 the original 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 "-- تلقائي --"
@@ -31184,11 +31368,12 @@ 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 ""
"لقد فشلت محاولتنا لتنفيذ عملية الدفع الخاصة بك عبر PayPal. يرجى المحاولة مرة "
"أخرى أو الاتصال بنا."
#: pretix/plugins/paypal2/views.py
msgid ""
@@ -34214,10 +34399,6 @@ msgstr[5] "تم استخدام هذه التذكرة %(count)s مرة."
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 "تم تخفيض سعر هذا المنتج بسبب خصم تلقائي."
@@ -34651,6 +34832,10 @@ msgstr "%(value)sبدون ضرائب"
msgid "Set price in %(currency)s for %(item)s"
msgstr "تحديد السعر بـ %(currency)s لـ %(item)s"
#: 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"
@@ -36381,6 +36566,13 @@ msgstr "الوصول إلى الكتابة"
msgid "Kosovo"
msgstr "كوسوفو"
#~ msgid ""
#~ "Our attempt to execute your payment via PayPal has failed. Please try "
#~ "again or contact us."
#~ msgstr ""
#~ "لقد فشلت محاولتنا لتنفيذ عملية الدفع الخاصة بك عبر PayPal. يرجى المحاولة "
#~ "مرة أخرى أو الاتصال بنا."
#~ msgid "All actions"
#~ msgstr "جميع الإجراءات"
@@ -36504,9 +36696,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"
@@ -37097,9 +37286,6 @@ msgstr "كوسوفو"
#~ msgid "Badge design"
#~ msgstr "تصميم شارة"
#~ msgid "(Same as above)"
#~ msgstr "(كما ورد أعلاه)"
#~ msgid "Translation in development"
#~ msgstr "الترجمة في التنمية"
+210 -53
View File
@@ -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 15:35+0000\n"
"PO-Revision-Date: 2024-12-11 01:00+0000\n"
"Last-Translator: Neriman Memmedli <nerim4n@pm.me>\n"
"Language-Team: Azerbaijani <https://translate.pretix.eu/projects/pretix/"
@@ -91,6 +91,10 @@ msgstr "Yunanca"
msgid "Hebrew"
msgstr ""
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr ""
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr "İndoneziya dili"
@@ -410,6 +414,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 ""
@@ -838,8 +846,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
@@ -856,7 +863,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 ""
@@ -1587,9 +1593,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
@@ -1601,8 +1606,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 ""
@@ -1610,8 +1614,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 ""
@@ -1634,8 +1637,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"
@@ -1663,8 +1665,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"
@@ -2451,7 +2452,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
@@ -2956,13 +2958,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
@@ -2995,6 +2995,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 "
@@ -5321,6 +5331,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 ""
@@ -5358,6 +5372,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 "
@@ -5957,10 +5975,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 ""
@@ -6869,7 +6883,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
@@ -7596,6 +7610,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 ""
@@ -7620,6 +7642,26 @@ 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr ""
@@ -8705,7 +8747,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 ""
@@ -9286,6 +9328,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 ""
@@ -12427,6 +12488,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 ""
@@ -18577,6 +18654,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 ""
@@ -20228,6 +20306,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 ""
@@ -20244,6 +20340,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
@@ -20766,6 +20882,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 ""
@@ -20793,6 +20916,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"
@@ -21048,25 +21188,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
@@ -22304,6 +22429,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 ""
@@ -25179,10 +25313,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 ""
@@ -25328,7 +25458,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 ""
@@ -25718,7 +25848,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."
@@ -27762,6 +27893,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 the original 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 ""
@@ -27987,8 +28141,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
@@ -30721,10 +30878,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 ""
@@ -31122,6 +31275,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"
+241 -63
View File
@@ -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 15:35+0000\n"
"PO-Revision-Date: 2026-06-25 14:00+0000\n"
"Last-Translator: Kim Lozano <joaquim.lozano@upc.edu>\n"
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix/"
@@ -91,6 +91,10 @@ msgstr "Grec"
msgid "Hebrew"
msgstr ""
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr ""
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr ""
@@ -460,6 +464,12 @@ msgstr ""
msgid "You cannot exchange a medium for a medium."
msgstr "No podeu modificar aquesta comanda."
#: pretix/api/views/checkin.py
#, fuzzy
#| msgid "You cannot change this order."
msgid "You cannot simulate a medium exchange."
msgstr "No podeu modificar aquesta comanda."
#: pretix/api/views/oauth.py pretix/control/logdisplay.py
#, python-brace-format
msgid ""
@@ -959,8 +969,7 @@ msgstr "Enviar registre"
#: 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
@@ -979,7 +988,6 @@ msgstr "Nom de l'assistent"
#: 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 "Correu electrònic de l'assistent"
@@ -1799,9 +1807,8 @@ msgid "Invoice sender:"
msgstr "Emissor de la factura:"
#: 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
@@ -1813,8 +1820,7 @@ msgstr "Adreça"
#: 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 "Codi postal"
@@ -1822,8 +1828,7 @@ msgstr "Codi 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 "Ciutat"
@@ -1846,8 +1851,7 @@ msgstr "Estat"
#: 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"
@@ -1875,8 +1879,7 @@ msgstr "Receptor de la factura:"
#: 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"
@@ -2735,7 +2738,8 @@ msgstr "Estats del pagament"
msgid "Refund states"
msgstr "Estats del reemborsament"
#: 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
@@ -3283,14 +3287,14 @@ msgstr ""
"està corromput."
#: pretix/base/forms/questions.py
msgid ""
"If you keep this empty, the ticket will be valid starting at the time of "
"purchase."
msgstr ""
msgid "Street"
msgstr "Carrer"
#: pretix/base/forms/questions.py
msgid "Street and Number"
msgstr "Carrer i número"
#, fuzzy
#| msgid "Status"
msgid "State"
msgstr "Estat"
#: pretix/base/forms/questions.py
#, fuzzy, python-brace-format
@@ -3327,6 +3331,16 @@ msgstr "Si us plau, introduïu un canal de venda vàlid."
msgid "Please enter a date and time no later than {max}."
msgstr "Si us plau, introduïu un nom més curt."
#: 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 "Carrer i número"
#: pretix/base/forms/questions.py
msgid ""
"Optional, but depending on the country you reside in we might need to charge "
@@ -6046,6 +6060,10 @@ msgstr "La variació triada no pertany a aquest element."
msgid "The count needs to be equal to or greater than zero."
msgstr "El recompte ha de ser igual o major que zero."
#: pretix/base/models/items.py pretix/base/models/orders.py
msgid "Order position"
msgstr "Posició de la comanda"
#: pretix/base/models/items.py
msgid "Number"
msgstr "Número"
@@ -6083,6 +6101,10 @@ msgstr "Dia i hora"
msgid "Country code (ISO 3166-1 alpha-2)"
msgstr "Codi de país (ISO 3166-1 alpha-2)"
#: pretix/base/models/items.py
msgid "Asked on"
msgstr ""
#: pretix/base/models/items.py pretix/base/models/organizer.py
#, fuzzy
#| msgid "The property name may only contain letters, numbers and underscores."
@@ -6748,10 +6770,6 @@ msgstr "Targeta regal"
msgid "Value"
msgstr "Valor"
#: pretix/base/models/orders.py
msgid "Order position"
msgstr "Posició de la comanda"
#: pretix/base/models/orders.py
msgid "Cart ID (e.g. session key)"
msgstr "Identificador del carretó (ex. clau de sessió)"
@@ -7771,7 +7789,7 @@ msgstr "Ja s'ha rebut el pagament per a aquesta factura."
#: 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
@@ -8633,6 +8651,18 @@ msgstr "Inici de la prevenda"
msgid "Presale end"
msgstr "Finalització de la prevenda"
#: pretix/base/reldate.py
#, fuzzy
#| msgid "Order details"
msgid "Order creation"
msgstr "Detalls de la comanda"
#: pretix/base/reldate.py
#, fuzzy
#| msgid "Order expired"
msgid "Order expiry"
msgstr "Ha expirat la comanda"
#: pretix/base/reldate.py
#, fuzzy
#| msgid "days before"
@@ -8659,6 +8689,26 @@ msgstr "Temps relatiu:"
msgid "Not set"
msgstr "No s'ha establert"
#: 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr "Aleatori (per defecte, funciona amb totes les apps de pretix)"
@@ -10003,7 +10053,7 @@ msgstr ""
"Si us plau, feu una transferència a aquest compte bancari: "
"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 ""
@@ -10743,6 +10793,25 @@ msgstr ""
"la finestra de temps de venda. Podeu utilitzar-ho per descriure altres "
"opcions per obtenir un tiquet, com per exemple una oficina."
#: 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 "Text de guia"
@@ -14757,6 +14826,30 @@ msgid ""
"card is issued in."
msgstr ""
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment date"
msgid "Payment term"
msgstr "Data de pagament"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "(Same as above)"
msgid "same as above"
msgstr "(Igual que aquí al damunt)"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment term in days"
msgid "different payment term in days"
msgstr "Termini de pagament en dies"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment term in minutes"
msgid "different payment term in minutes"
msgstr "Termini de pagament en minuts"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "including all taxes"
@@ -21746,6 +21839,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 "Use feature"
@@ -23594,6 +23688,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 "Crear una nova pregunta"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Create a new question"
msgid "Create a new order-level question"
msgstr "Crear una nova pregunta"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Delete question"
msgid "Per-ticket questions"
msgstr "Esborrar pregunta"
#: 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 "Crear una nova pregunta"
@@ -23612,6 +23730,30 @@ msgstr ""
msgid "All personalized products"
msgstr "Tots els productes d'admissió"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Required question"
msgid "Per-order questions"
msgstr "Pregunta obligatòria"
#: 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 task has been completed."
msgid "These questions are asked once per order."
msgstr "S'ha completat la tasca."
#: pretix/control/templates/pretixcontrol/items/quota.html
#: pretix/control/templates/pretixcontrol/items/quota_edit.html
#, python-format
@@ -24158,6 +24300,15 @@ msgstr "Dades de la factura"
msgid "(optional)"
msgstr "(opcional)"
#: 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 "Meta information"
msgid "Additional order information"
msgstr "Informació meta"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid "Delete order"
msgstr "Esborrar comanda"
@@ -24185,6 +24336,23 @@ msgstr "Sí, denegar la comanda"
msgid "Extend payment term"
msgstr "Ampliar el termini de pagament"
#: 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"
@@ -24479,25 +24647,10 @@ msgstr "Entrada: %(datetime)s"
msgid "Ticket page"
msgstr "Tiquet"
#: 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
@@ -25887,6 +26040,16 @@ msgstr "Connectar un dispositiu"
msgid "Hardware model"
msgstr "Model de hardware"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
#, fuzzy, python-format
#| msgid "Begin: %(time)s"
msgid "Last seen: %(time)s"
msgstr "Inici: %(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 "Encara no està inicialitzat"
@@ -29031,10 +29194,6 @@ msgstr ""
msgid "Not all objects have been selected."
msgstr "Aquesta posició de la comanda s'ha cancel·lat."
#: pretix/control/views/item.py
msgid "Street"
msgstr "Carrer"
#: pretix/control/views/item.py
msgid "The requested question does not exist."
msgstr ""
@@ -29182,7 +29341,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 ""
@@ -29616,7 +29775,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."
@@ -31911,6 +32071,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 the original 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 "Stripe account"
@@ -32177,8 +32360,11 @@ msgstr "Si us plau, trieu com voleu fer el pagament."
#: 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
@@ -35320,10 +35506,6 @@ msgstr[1] "Ja s'ha validat aquest tiquet."
msgid "No attendee name provided"
msgstr "Mostres els noms dels assistents a les factures"
#: 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 ""
@@ -35779,6 +35961,10 @@ msgstr "Valor total (sense imposts)"
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"
@@ -37705,11 +37891,6 @@ msgstr "Kosovo"
#~ msgid "Please do not use special characters in names."
#~ msgstr "Si us plau, introduïu un nom més curt."
#, fuzzy
#~| msgid "Status"
#~ msgid "State"
#~ msgstr "Estat"
#, fuzzy
#~| msgid "Printing date and time"
#~ msgid "Program times: date and time"
@@ -38154,9 +38335,6 @@ msgstr "Kosovo"
#~ msgid "Badge design"
#~ msgstr "Disseny de la tarja d'identificació"
#~ msgid "(Same as above)"
#~ msgstr "(Igual que aquí al damunt)"
#~ msgid "Translation in development"
#~ msgstr "Traducció en progrés"
+249 -66
View File
@@ -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 15:35+0000\n"
"PO-Revision-Date: 2026-01-29 19:42+0000\n"
"Last-Translator: Jiří Pastrňák <jiri@pastrnak.email>\n"
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix/cs/"
@@ -91,6 +91,10 @@ msgstr "Řečtina"
msgid "Hebrew"
msgstr "Hebrejština"
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr ""
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr "Indonéština"
@@ -438,6 +442,12 @@ msgstr "Médium je napojené na jinou událost"
msgid "You cannot exchange a medium for a medium."
msgstr "Tuto objednávku nelze změnit."
#: pretix/api/views/checkin.py
#, fuzzy
#| msgid "You cannot change this order."
msgid "You cannot simulate a medium exchange."
msgstr "Tuto objednávku nelze změnit."
#: pretix/api/views/oauth.py pretix/control/logdisplay.py
#, python-brace-format
msgid ""
@@ -901,8 +911,7 @@ msgstr "Odeslat informace o obnovení"
#: 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
@@ -921,7 +930,6 @@ msgstr "Jméno účastníka"
#: 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 účastníka"
@@ -1708,9 +1716,8 @@ msgid "Invoice sender:"
msgstr "Odesílatel 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
@@ -1722,8 +1729,7 @@ msgstr "Adresa"
#: 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 "PSČ"
@@ -1731,8 +1737,7 @@ msgstr "PSČ"
#: 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 "Město"
@@ -1755,8 +1760,7 @@ msgstr "Stát"
#: 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"
@@ -1784,8 +1788,7 @@ msgstr "Příjemce 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"
@@ -2598,7 +2601,8 @@ msgstr "Stavy platby"
msgid "Refund states"
msgstr "Stavy vrácení peněz"
#: 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
@@ -3117,16 +3121,12 @@ msgstr ""
"byl poškozený."
#: pretix/base/forms/questions.py
msgid ""
"If you keep this empty, the ticket will be valid starting at the time of "
"purchase."
msgstr ""
"Pokud toto políčko necháte prázdné, bude vstupenka platná od okamžiku "
"zakoupení."
msgid "Street"
msgstr "Ulice"
#: pretix/base/forms/questions.py
msgid "Street and Number"
msgstr "Ulice a číslo popisné"
msgid "State"
msgstr "Stav"
#: pretix/base/forms/questions.py
#, fuzzy, python-brace-format
@@ -3163,6 +3163,18 @@ msgstr "Zadejte prosím platný prodejní kanál."
msgid "Please enter a date and time no later than {max}."
msgstr "Zadejte prosím správný výsledek."
#: pretix/base/forms/questions.py
msgid ""
"If you keep this empty, the ticket will be valid starting at the time of "
"purchase."
msgstr ""
"Pokud toto políčko necháte prázdné, bude vstupenka platná od okamžiku "
"zakoupení."
#: pretix/base/forms/questions.py
msgid "Street and Number"
msgstr "Ulice a číslo popisné"
#: pretix/base/forms/questions.py
msgid ""
"Optional, but depending on the country you reside in we might need to charge "
@@ -5725,6 +5737,10 @@ msgstr "Tato varianta nepatří k tomuto produktu."
msgid "The count needs to be equal to or greater than zero."
msgstr "Počet musí být alespoň nula."
#: pretix/base/models/items.py pretix/base/models/orders.py
msgid "Order position"
msgstr "Objednaný produkt"
#: pretix/base/models/items.py
msgid "Number"
msgstr "Číslo"
@@ -5762,6 +5778,10 @@ msgstr "Datum a čas"
msgid "Country code (ISO 3166-1 alpha-2)"
msgstr "Země (ISO3166-1-alpha-2-kód zemí)"
#: 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 "
@@ -6420,10 +6440,6 @@ msgstr "Dárkový poukaz"
msgid "Value"
msgstr "Hodnota"
#: pretix/base/models/orders.py
msgid "Order position"
msgstr "Objednaný produkt"
#: pretix/base/models/orders.py
msgid "Cart ID (e.g. session key)"
msgstr "ID košíku (např. klíč relace)"
@@ -7414,7 +7430,7 @@ msgstr "Platba za tuto fakturu již byla přijata."
#, fuzzy
#| msgid "This payment can not be canceled at the moment."
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 "Tuto platbu nelze dočasně zrušit."
#: pretix/base/payment.py
@@ -8212,6 +8228,18 @@ msgstr "Zahájení předprodeje"
msgid "Presale end"
msgstr "Konec předprodeje"
#: pretix/base/reldate.py
#, fuzzy
#| msgid "Order details"
msgid "Order creation"
msgstr "Podrobnosti o objednávce"
#: pretix/base/reldate.py
#, fuzzy
#| msgid "Order expired"
msgid "Order expiry"
msgstr "Objednávka vypršela"
#: pretix/base/reldate.py
msgid "before"
msgstr "před"
@@ -8236,6 +8264,26 @@ msgstr "Relativní čas:"
msgid "Not set"
msgstr "Nenastaveno"
#: 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr "Náhodný (výchozí, funguje se všemi aplikacemi pretix)"
@@ -9529,7 +9577,7 @@ msgstr "Částka byla stržena z vaší karty."
msgid "Please transfer money to this bank account: 9999-9999-9999-9999"
msgstr "Peníze prosím převeďte na tento bankovní účet: 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 ""
@@ -10228,6 +10276,25 @@ msgstr ""
"prodeje pro tuto akci. Můžete jej použít k popisu dalších možností, jak "
"získat vstupenku, například na pokladně."
#: 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 "Nápověda"
@@ -14111,6 +14178,30 @@ msgstr ""
"Nastavili jste platnost dárkových karet na {} let plus rok vydání dárkové "
"karty."
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment date"
msgid "Payment term"
msgstr "Datum splatnosti"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "(Same as above)"
msgid "same as above"
msgstr "(Stejně jako výše)"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment term in days"
msgid "different payment term in days"
msgstr "Doba splatnosti ve dnech"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment term in minutes"
msgid "different payment term in minutes"
msgstr "Platební lhůta v minutách"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Price including add-ons"
@@ -20975,6 +21066,7 @@ msgid "Top recommendation"
msgstr "Doporučené rozšíření"
#: 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 "Experimentální funkce"
@@ -22720,6 +22812,30 @@ msgid ""
"dietary requirements."
msgstr ""
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Create a new customer"
msgid "Create a new per-ticket question"
msgstr "Vytvořit nového zákazníka"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Create a new event"
msgid "Create a new order-level question"
msgstr "Vytvořit novou událost"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Delete question"
msgid "Per-ticket questions"
msgstr "Smazat otázku"
#: 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 ""
@@ -22736,6 +22852,30 @@ msgstr ""
msgid "All personalized products"
msgstr "Všechny personalizované produkty"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Include questions"
msgid "Per-order questions"
msgstr "Zobrazit otázky"
#: 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 "Otázka byla přeřazena."
#: pretix/control/templates/pretixcontrol/items/quota.html
#: pretix/control/templates/pretixcontrol/items/quota_edit.html
#, python-format
@@ -23276,6 +23416,15 @@ msgstr "Fakturační údaje"
msgid "(optional)"
msgstr "(volitelné)"
#: 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 "Další informace"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid "Delete order"
msgstr ""
@@ -23303,6 +23452,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 "NEZABEZPEČENÉ"
#: 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 "není odpovězeno"
#: pretix/control/templates/pretixcontrol/order/index.html
#, python-format
msgid "Order details: %(code)s"
@@ -23579,25 +23745,10 @@ msgstr "Platný %(datetime_range)s"
msgid "Ticket page"
msgstr ""
#: pretix/control/templates/pretixcontrol/order/index.html
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html
msgid "not answered"
msgstr "není odpovězeno"
#: 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 "NEZABEZPEČENÉ"
#: pretix/control/templates/pretixcontrol/order/index.html
#: pretix/control/templates/pretixcontrol/orders/overview.html
#: pretix/plugins/reports/accountingreport.py
@@ -24925,6 +25076,16 @@ msgstr "Připojit zařízení"
msgid "Hardware model"
msgstr "Model hardwaru"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
#, fuzzy, python-format
#| msgid "Begin: %(time)s"
msgid "Last seen: %(time)s"
msgstr "Začá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 "Zatím neinicializováno"
@@ -27992,10 +28153,6 @@ msgstr ""
msgid "Not all objects have been selected."
msgstr ""
#: pretix/control/views/item.py
msgid "Street"
msgstr "Ulice"
#: pretix/control/views/item.py
msgid "The requested question does not exist."
msgstr ""
@@ -28187,7 +28344,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 ""
@@ -28593,7 +28750,8 @@ msgstr "Předmět: {subject}"
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."
@@ -30772,6 +30930,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 the original 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 "-- Automaticky --"
@@ -31009,13 +31190,13 @@ msgid "Please select how you want to pay."
msgstr "Vyberte, jak chcete platit."
#: 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."
"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 ""
"Náš pokus o provedení vaší platby přes PayPal se nezdařil. Zkuste to prosím "
"znovu nebo kontaktujte nás."
#: pretix/plugins/paypal2/views.py
msgid ""
@@ -33950,10 +34131,6 @@ msgstr[2] "Vstupenka byla použita %(count)s krát."
msgid "No attendee name provided"
msgstr "Nebylo uvedeno žádné jméno účastníka"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html
msgid "The image you previously uploaded"
msgstr "Obrázek, který jste předtím nahráli"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html
msgid "The price of this product was reduced because of an automatic discount."
msgstr "Cena tohoto produktu byla snížena díky automatické slevě."
@@ -34380,6 +34557,10 @@ msgstr "%(value)s Celková cena (bez DPH)"
msgid "Set price in %(currency)s for %(item)s"
msgstr "Nastavení ceny v %(currency)s pro %(item)s"
#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html
msgid "The image you previously uploaded"
msgstr "Obrázek, který jste předtím nahráli"
#: pretix/presale/templates/pretixpresale/event/fragment_quota_left.html
#, python-format
msgid "%(num)s currently available"
@@ -36150,6 +36331,14 @@ msgstr "Přístup k zápisu"
msgid "Kosovo"
msgstr "Kosovo"
#, fuzzy
#~ msgid ""
#~ "Our attempt to execute your payment via PayPal has failed. Please try "
#~ "again or contact us."
#~ msgstr ""
#~ "Náš pokus o provedení vaší platby přes PayPal se nezdařil. Zkuste to "
#~ "prosím znovu nebo kontaktujte nás."
#~ msgid "All actions"
#~ msgstr "Všechny akce"
@@ -36247,9 +36436,6 @@ msgstr "Kosovo"
#~ msgid "Please do not use special characters in names."
#~ msgstr "Ve jméně není dovoleno používat speciální znaky."
#~ msgid "State"
#~ msgstr "Stav"
#, fuzzy
#~| msgid "Printing date and time"
#~ msgid "Program times: date and time"
@@ -36796,9 +36982,6 @@ msgstr "Kosovo"
#~ msgid "minutes before"
#~ msgstr "Minuty před"
#~ msgid "(Same as above)"
#~ msgstr "(Stejně jako výše)"
#~ msgid "Translation in development"
#~ msgstr "Překlad je v průběhu"
+220 -54
View File
@@ -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 15:35+0000\n"
"PO-Revision-Date: 2023-11-14 11:00+0000\n"
"Last-Translator: Charliecoleg <DM229135@colegsirgar.ac.uk>\n"
"Language-Team: Welsh <https://translate.pretix.eu/projects/pretix/pretix/cy/"
@@ -92,6 +92,10 @@ msgstr "Groeg"
msgid "Hebrew"
msgstr ""
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr ""
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr "Indonesaidd"
@@ -415,6 +419,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 ""
@@ -853,8 +861,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
@@ -871,7 +878,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 ""
@@ -1609,9 +1615,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
@@ -1623,8 +1628,7 @@ msgstr "Cyfeiriad"
#: 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 ""
@@ -1632,8 +1636,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 "Dinas"
@@ -1656,8 +1659,7 @@ msgstr "Talaith"
#: 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"
@@ -1685,8 +1687,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"
@@ -2473,7 +2474,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
@@ -2978,14 +2980,15 @@ 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"
msgstr ""
#, fuzzy
#| msgctxt "address"
#| msgid "State"
msgid "State"
msgstr "Talaith"
#: pretix/base/forms/questions.py
#, python-brace-format
@@ -3017,6 +3020,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 "
@@ -5349,6 +5362,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 ""
@@ -5386,6 +5403,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 "
@@ -5985,10 +6006,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 ""
@@ -6904,7 +6921,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
@@ -7640,6 +7657,16 @@ msgstr ""
msgid "Presale end"
msgstr ""
#: pretix/base/reldate.py
#, fuzzy
#| msgid "Create"
msgid "Order creation"
msgstr "Creu"
#: pretix/base/reldate.py
msgid "Order expiry"
msgstr ""
#: pretix/base/reldate.py
msgid "before"
msgstr ""
@@ -7664,6 +7691,26 @@ 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr ""
@@ -8761,7 +8808,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 ""
@@ -9342,6 +9389,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 ""
@@ -12498,6 +12564,24 @@ msgid ""
"card is issued in."
msgstr ""
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment"
msgid "Payment term"
msgstr "Taliad"
#: 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 ""
@@ -18686,6 +18770,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 ""
@@ -20370,6 +20455,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 ""
@@ -20386,6 +20489,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
@@ -20914,6 +21037,15 @@ 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
#, fuzzy
#| msgid "Additional fee"
msgid "Additional order information"
msgstr "Ffi ychwanegol"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid "Delete order"
msgstr ""
@@ -20941,6 +21073,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 "ANNIOGEL"
#: 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"
@@ -21196,25 +21345,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 "ANNIOGEL"
#: pretix/control/templates/pretixcontrol/order/index.html
#: pretix/control/templates/pretixcontrol/orders/overview.html
#: pretix/plugins/reports/accountingreport.py
@@ -22466,6 +22600,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 ""
@@ -25367,10 +25510,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 ""
@@ -25524,7 +25663,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 ""
@@ -25914,7 +26053,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."
@@ -27971,6 +28111,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 the original 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 ""
@@ -28196,8 +28359,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
@@ -30946,10 +31112,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 ""
@@ -31356,6 +31518,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"
+243 -63
View File
@@ -3,11 +3,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 15:35+0000\n"
"PO-Revision-Date: 2026-07-31 18:00+0000\n"
"Last-Translator: Nikolai <nikolai@lengefeldt.de>\n"
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix/"
"da/>\n"
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix/da/"
">\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,26 @@ 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr "Tilfældigt (standard, virker med alle Pretix-apps)"
@@ -9488,7 +9540,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 +10248,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 +14191,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 +20934,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 +22714,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 +22756,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 +23348,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 +23390,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 +23686,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 +25058,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 +28210,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 +28360,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 +28794,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 +31037,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 the original 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 +31307,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 +34351,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 +34776,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 +36644,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"
+260 -75
View File
@@ -4,8 +4,8 @@ msgid ""
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-30 18:31+0000\n"
"POT-Creation-Date: 2026-08-24 15:35+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix/"
"de/>\n"
@@ -14,7 +14,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"
"X-Poedit-Bookmarks: -1,-1,904,-1,-1,-1,-1,-1,-1,-1\n"
#: pretix/_base_settings.py
@@ -89,6 +89,10 @@ msgstr "Griechisch"
msgid "Hebrew"
msgstr "Hebräisch"
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr "Ungarisch"
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr "Indonesisch"
@@ -429,6 +433,10 @@ 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
msgid "You cannot simulate a medium exchange."
msgstr "Ein Medientausch kann nicht simuliert werden."
#: pretix/api/views/oauth.py pretix/control/logdisplay.py
#, python-brace-format
msgid ""
@@ -883,8 +891,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 +908,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 +1654,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 +1667,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 +1675,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 +1698,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 +1726,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 +2542,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 +3065,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 +3102,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 +5756,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 +5797,10 @@ 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
msgid "Asked on"
msgstr "Gefragt bei"
#: pretix/base/models/items.py pretix/base/models/organizer.py
msgid ""
"The identifier may only contain letters, numbers, dots, dashes, and "
@@ -6445,10 +6463,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)"
@@ -7488,7 +7502,7 @@ msgstr "Die Rechnung wurde bereits beglichen."
#: 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 ""
"Diese Zahlung wird bereits verarbeitet und kann nicht mehr abgebrochen "
"werden."
@@ -8267,6 +8281,14 @@ msgstr "Start des Vorverkaufs"
msgid "Presale end"
msgstr "Ende des Vorverkaufs"
#: pretix/base/reldate.py
msgid "Order creation"
msgstr "Bestelldatum"
#: pretix/base/reldate.py
msgid "Order expiry"
msgstr "Zahlungsfrist"
#: pretix/base/reldate.py
msgid "before"
msgstr "bevor"
@@ -8291,6 +8313,26 @@ 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 "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/reldate.py
#, python-brace-format
msgid "A relative time cannot be expressed as \"before\" for \"{}\""
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 "Ein relativer Zeitpunkt 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)"
@@ -9591,7 +9633,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 +10348,30 @@ 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 "Keine Termine passen zu Ihren Kriterien."
#: pretix/base/settings.py
msgctxt "subevents"
msgid "Text for empty date results"
msgstr "Text für leere Terminanzeige"
#: 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 ""
"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"
msgstr "Hilfetext"
@@ -14287,6 +14353,22 @@ msgstr ""
"Sie haben eingestellt, dass Wertgutscheine {} Jahre nach Ende des Jahres der "
"Ausstellung ablaufen."
#: pretix/control/forms/event.py
msgid "Payment term"
msgstr "Zahlungsfrist"
#: pretix/control/forms/event.py
msgid "same as above"
msgstr "wie oben"
#: pretix/control/forms/event.py
msgid "different payment term in days"
msgstr "abweichendes Zahlungsziel in Tagen"
#: pretix/control/forms/event.py
msgid "different payment term in minutes"
msgstr "abweichendes Zahlungsziel in Minuten"
#: pretix/control/forms/event.py
msgid "Prices including tax"
msgstr "Preis inklusive Steuern"
@@ -16850,8 +16932,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 +21286,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 +23198,26 @@ 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
msgid "Create a new per-ticket question"
msgstr "Neue Frage für jedes Ticket erstellen"
#: pretix/control/templates/pretixcontrol/items/questions.html
msgid "Create a new order-level question"
msgstr "Neue Frage für jede Bestellung erstellen"
#: pretix/control/templates/pretixcontrol/items/questions.html
msgid "Per-ticket questions"
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"
msgstr "Neue Frage erstellen"
@@ -23131,6 +23234,30 @@ msgstr "Beim Check-in fragen"
msgid "All personalized products"
msgstr "Alle personalisierten Produkte"
#: pretix/control/templates/pretixcontrol/items/questions.html
msgid "Per-order questions"
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
msgid "These questions are asked once per order."
msgstr "Diese Fragen werden einmal pro Bestellung gestellt."
#: pretix/control/templates/pretixcontrol/items/quota.html
#: pretix/control/templates/pretixcontrol/items/quota_edit.html
#, python-format
@@ -23735,6 +23862,13 @@ 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
msgid "Additional order information"
msgstr "Zusätzliche Bestellinformationen"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid "Delete order"
msgstr "Bestellung löschen"
@@ -23764,6 +23898,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 +24201,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 +25576,15 @@ msgstr "Neues Gerät verbinden"
msgid "Hardware model"
msgstr "Hardware-Modell"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
#, python-format
msgid "Last seen: %(time)s"
msgstr "Zuletzt gesehen: %(time)s Uhr"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
msgid "No recent contact"
msgstr "Kein kürzlicher Kontakt"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
msgid "Not yet initialized"
msgstr "Noch nicht installiert"
@@ -28735,10 +28880,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."
@@ -28913,11 +29054,11 @@ 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 ""
"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."
@@ -29377,7 +29518,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 +31800,37 @@ 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 "Erlaube weitere Zahlungsversuche während PayPal eine Zahlung überprüft"
#: 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 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 "
"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 "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 --"
msgstr "-- Automatisch --"
@@ -31928,11 +32101,18 @@ 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."
"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 ""
@@ -34989,10 +35169,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 +35610,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 +37417,21 @@ 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."
#~ 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 +37581,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 +38308,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 +39006,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"
+2
View File
@@ -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
@@ -7,8 +7,8 @@ msgid ""
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-30 18:31+0000\n"
"POT-Creation-Date: 2026-08-24 15:35+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
"pretix/pretix/de_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"
@@ -91,6 +91,10 @@ msgstr "Griechisch"
msgid "Hebrew"
msgstr "Hebräisch"
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr "Ungarisch"
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr "Indonesisch"
@@ -431,6 +435,10 @@ msgstr "Medium ist mit anderer Veranstaltung verbunden"
msgid "You cannot exchange a medium for a medium."
msgstr "Du kannst kein Medium gegen ein Medium eintauschen."
#: pretix/api/views/checkin.py
msgid "You cannot simulate a medium exchange."
msgstr "Ein Medientausch kann nicht simuliert werden."
#: pretix/api/views/oauth.py pretix/control/logdisplay.py
#, python-brace-format
msgid ""
@@ -885,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
@@ -903,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"
@@ -1650,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
@@ -1664,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"
@@ -1673,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"
@@ -1697,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"
@@ -1726,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"
@@ -2543,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
@@ -3065,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 du dieses Feld leer lässt, 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
@@ -3105,6 +3104,17 @@ msgstr "Bitte gib einen Zeitpunkt nicht vor {min} ein."
msgid "Please enter a date and time no later than {max}."
msgstr "Bitte gib 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 du dieses Feld leer lässt, 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 "
@@ -5743,6 +5753,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"
@@ -5780,6 +5794,10 @@ 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
msgid "Asked on"
msgstr "Gefragt bei"
#: pretix/base/models/items.py pretix/base/models/organizer.py
msgid ""
"The identifier may only contain letters, numbers, dots, dashes, and "
@@ -6442,10 +6460,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)"
@@ -7481,7 +7495,7 @@ msgstr "Die Rechnung wurde bereits beglichen."
#: 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 ""
"Diese Zahlung wird bereits verarbeitet und kann nicht mehr abgebrochen "
"werden."
@@ -8260,6 +8274,14 @@ msgstr "Start des Vorverkaufs"
msgid "Presale end"
msgstr "Ende des Vorverkaufs"
#: pretix/base/reldate.py
msgid "Order creation"
msgstr "Bestelldatum"
#: pretix/base/reldate.py
msgid "Order expiry"
msgstr "Zahlungsfrist"
#: pretix/base/reldate.py
msgid "before"
msgstr "bevor"
@@ -8284,6 +8306,26 @@ 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 "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/reldate.py
#, python-brace-format
msgid "A relative time cannot be expressed as \"before\" for \"{}\""
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 "Ein relativer Zeitpunkt 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)"
@@ -9579,7 +9621,7 @@ msgid "Please transfer money to this bank account: 9999-9999-9999-9999"
msgstr ""
"Bitte überweise den vollen Betrag auf das 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 ""
"Dieser Textbaustein wird aufgrund verschiedener Parameter dynamisch "
@@ -10294,6 +10336,30 @@ msgstr ""
"Verkaufszeitraum beendet ist. Du kannst hier andere Wege ein Ticket zu "
"bekommen, wie z.B. eine Abendkasse, beschreiben."
#: pretix/base/settings.py
msgid "No dates match your criteria."
msgstr "Keine Termine passen zu deinen Kriterien."
#: pretix/base/settings.py
msgctxt "subevents"
msgid "Text for empty date results"
msgstr "Text für leere Terminanzeige"
#: 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 ""
"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"
msgstr "Hilfetext"
@@ -14263,6 +14329,22 @@ msgstr ""
"Du hast eingestellt, dass Wertgutscheine {} Jahre nach Ende des Jahres der "
"Ausstellung ablaufen."
#: pretix/control/forms/event.py
msgid "Payment term"
msgstr "Zahlungsfrist"
#: pretix/control/forms/event.py
msgid "same as above"
msgstr "wie oben"
#: pretix/control/forms/event.py
msgid "different payment term in days"
msgstr "abweichendes Zahlungsziel in Tagen"
#: pretix/control/forms/event.py
msgid "different payment term in minutes"
msgstr "abweichendes Zahlungsziel in Minuten"
#: pretix/control/forms/event.py
msgid "Prices including tax"
msgstr "Preis inklusive Steuern"
@@ -16827,8 +16909,8 @@ msgid ""
"%(max_redeemed)s times."
msgstr ""
"Du kannst 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 ""
@@ -21176,6 +21258,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"
@@ -23085,6 +23168,26 @@ msgstr ""
"Ticket auszufüllen. Wenn deine Veranstaltung Verpflegung beinhaltet, "
"könntest du z.B. nach Allergien deiner Teilnehmer*innen fragen."
#: pretix/control/templates/pretixcontrol/items/questions.html
msgid "Create a new per-ticket question"
msgstr "Neue Frage für jedes Ticket erstellen"
#: pretix/control/templates/pretixcontrol/items/questions.html
msgid "Create a new order-level question"
msgstr "Neue Frage für jede Bestellung erstellen"
#: pretix/control/templates/pretixcontrol/items/questions.html
msgid "Per-ticket questions"
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"
msgstr "Neue Frage erstellen"
@@ -23101,6 +23204,30 @@ msgstr "Beim Check-in fragen"
msgid "All personalized products"
msgstr "Alle personalisierten Produkte"
#: pretix/control/templates/pretixcontrol/items/questions.html
msgid "Per-order questions"
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
msgid "These questions are asked once per order."
msgstr "Diese Fragen werden einmal pro Bestellung gestellt."
#: pretix/control/templates/pretixcontrol/items/quota.html
#: pretix/control/templates/pretixcontrol/items/quota_edit.html
#, python-format
@@ -23705,6 +23832,13 @@ 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
msgid "Additional order information"
msgstr "Zusätzliche Bestellinformationen"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid "Delete order"
msgstr "Bestellung löschen"
@@ -23734,6 +23868,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"
@@ -24017,27 +24170,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
@@ -25406,6 +25542,15 @@ msgstr "Neues Gerät verbinden"
msgid "Hardware model"
msgstr "Hardware-Modell"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
#, python-format
msgid "Last seen: %(time)s"
msgstr "Zuletzt gesehen: %(time)s Uhr"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
msgid "No recent contact"
msgstr "Kein kürzlicher Kontakt"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
msgid "Not yet initialized"
msgstr "Noch nicht installiert"
@@ -28694,10 +28839,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."
@@ -28872,13 +29013,13 @@ 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 ""
"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."
@@ -29334,7 +29475,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."
@@ -31614,6 +31756,37 @@ msgstr ""
"Limit) in mehreren Raten oder binnen 30 Tagen zu bezahlen. Du, als der "
"Händler, erhältst dein Geld jedoch sofort."
#: pretix/plugins/paypal2/payment.py
msgid "Allow further payments during compliance hold"
msgstr "Erlaube weitere Zahlungsversuche während PayPal eine Zahlung überprüft"
#: 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 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 "
"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 "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 --"
msgstr "-- Automatisch --"
@@ -31882,11 +32055,17 @@ msgstr "Bitte wähle 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 "
"probiere es erneut oder kontaktiere uns."
"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 ""
@@ -34935,10 +35114,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 ""
@@ -35378,6 +35553,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"
@@ -37180,6 +37359,21 @@ 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."
#~ msgstr ""
#~ "Unser Versuch, die Zahlung mit PayPal auszuführen, ist gescheitert. Bitte "
#~ "probiere es erneut oder kontaktiere uns."
#~ msgid "All actions"
#~ msgstr "Alle Aktionen"
@@ -37329,9 +37523,6 @@ msgstr "Kosovo"
#~ msgid "Please do not use special characters in names."
#~ msgstr "Bitte verwende keine Sonderzeichen in Namen."
#~ msgid "State"
#~ msgstr "Zustand"
#~ msgid "Program times: date and time"
#~ msgstr "Programmzeiten: Datum und Uhrzeit"
@@ -38062,9 +38253,6 @@ msgstr "Kosovo"
#~ msgid "Badge design"
#~ msgstr "Badge-Design"
#~ msgid "(Same as above)"
#~ msgstr "(wie oben)"
#~ msgid "Translation in development"
#~ msgstr "Übersetzung in Arbeit"
@@ -38706,10 +38894,6 @@ msgstr "Kosovo"
#~ msgstr ""
#~ "Wir haben dir in den letzten 24 Stunden bereits eine E-Mail geschickt."
#~ msgctxt "attendee_data"
#~ msgid "Asked"
#~ msgstr "Abfragen"
#~ msgid "Underpaid"
#~ msgstr "Unterzahlt"
@@ -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
+210 -53
View File
@@ -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 15:35+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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,26 @@ 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr ""
@@ -8704,7 +8746,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 +9327,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 +12487,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 +18653,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 +20305,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 +20339,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 +20881,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 +20915,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 +21187,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 +22428,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 +25312,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 +25457,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 +25847,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 +27890,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 the original 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 +28138,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 +30875,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 +31272,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"
+1 -1
View File
@@ -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 15:35+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
+256 -73
View File
@@ -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 15:35+0000\n"
"PO-Revision-Date: 2026-02-25 23:00+0000\n"
"Last-Translator: David Ibáñez Cerdeira <dibanez@gmail.com>\n"
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix/el/"
@@ -93,6 +93,10 @@ msgstr ""
msgid "Hebrew"
msgstr ""
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr ""
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr ""
@@ -471,6 +475,12 @@ msgstr ""
msgid "You cannot exchange a medium for a medium."
msgstr "Δεν μπορείτε να ακυρώσετε αυτήν την παραγγελία."
#: pretix/api/views/checkin.py
#, fuzzy
#| msgid "You cannot cancel this order."
msgid "You cannot simulate a medium exchange."
msgstr "Δεν μπορείτε να ακυρώσετε αυτήν την παραγγελία."
#: pretix/api/views/oauth.py pretix/control/logdisplay.py
#, python-brace-format
msgid ""
@@ -989,8 +999,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
@@ -1009,7 +1018,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 "Email συμμετεχόντος"
@@ -1849,9 +1857,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
@@ -1863,8 +1870,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 "Ταχυδρομικός Κώδικας"
@@ -1872,8 +1878,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 "Πόλη"
@@ -1898,8 +1903,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"
@@ -1927,8 +1931,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"
@@ -2811,7 +2814,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
@@ -3377,14 +3381,16 @@ msgid ""
msgstr ""
#: pretix/base/forms/questions.py
msgid ""
"If you keep this empty, the ticket will be valid starting at the time of "
"purchase."
msgstr ""
#, fuzzy
#| msgid "Street address"
msgid "Street"
msgstr "Διεύθυνση"
#: pretix/base/forms/questions.py
msgid "Street and Number"
msgstr "Οδός και Αριθμός"
#, fuzzy
#| msgid "Status"
msgid "State"
msgstr "Κατάσταση"
#: pretix/base/forms/questions.py
#, fuzzy, python-brace-format
@@ -3421,6 +3427,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 "
@@ -6273,6 +6289,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 "Αριθμός"
@@ -6310,6 +6330,10 @@ msgstr "Ημερομηνία και ώρα"
msgid "Country code (ISO 3166-1 alpha-2)"
msgstr "Κωδικός χώρας (ISO 3166-1 alpha-2)"
#: pretix/base/models/items.py
msgid "Asked on"
msgstr ""
#: pretix/base/models/items.py pretix/base/models/organizer.py
#, fuzzy
#| msgid "The property name may only contain letters, numbers and underscores."
@@ -6995,10 +7019,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 ""
@@ -8066,7 +8086,7 @@ msgstr "Η πληρωμή για αυτό το τιμολόγιο έχει ήδ
#, fuzzy
#| msgid "This payment can not be canceled at the moment."
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
@@ -8960,6 +8980,18 @@ msgstr "Έναρξη presale"
msgid "Presale end"
msgstr "Τέλος presale"
#: pretix/base/reldate.py
#, fuzzy
#| msgid "Order details"
msgid "Order creation"
msgstr "Λεπτομέρειες παραγγελίας"
#: pretix/base/reldate.py
#, fuzzy
#| msgid "Order expired"
msgid "Order expiry"
msgstr "Η παραγγελία έληξε"
#: pretix/base/reldate.py
#, fuzzy
#| msgid "days before"
@@ -8988,6 +9020,26 @@ 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr ""
@@ -10378,7 +10430,7 @@ msgid "Please transfer money to this bank account: 9999-9999-9999-9999"
msgstr ""
"Μεταφέρετε χρήματα σε αυτόν τον τραπεζικό λογαριασμό: 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 ""
@@ -11124,6 +11176,25 @@ 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 "Κείμενο καθοδήγησης"
@@ -15230,6 +15301,30 @@ msgid ""
"card is issued in."
msgstr ""
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment date"
msgid "Payment term"
msgstr "Ημερομηνία πληρωμής"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "(Same as above)"
msgid "same as above"
msgstr "(Ιδια όπως παραπάνω)"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment term in days"
msgid "different payment term in days"
msgstr "Διάρκεια πληρωμής σε ημέρες"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment term in days"
msgid "different payment term in minutes"
msgstr "Διάρκεια πληρωμής σε ημέρες"
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "including all taxes"
@@ -22706,6 +22801,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 "Features"
@@ -24761,6 +24857,30 @@ msgstr ""
"δεδομένα σχετικά με το εισιτήριό τους. Εάν παρέχετε τρόφιμα, ένα παράδειγμα "
"μπορεί να είναι να ζητήσετε από τους χρήστες σας τις διατροφικές απαιτήσεις."
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Create a new question"
msgid "Create a new per-ticket question"
msgstr "Δημιουργία μιας νέας ερώτησης"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Create a new question"
msgid "Create a new order-level question"
msgstr "Δημιουργία μιας νέας ερώτησης"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Delete question"
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 "Δημιουργία μιας νέας ερώτησης"
@@ -24781,6 +24901,30 @@ msgstr ""
msgid "All personalized products"
msgstr "Ολα τα προϊόντα"
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Include questions"
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
#, fuzzy
#| msgid "The question has been deleted."
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
@@ -25385,6 +25529,15 @@ 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
#, fuzzy
#| msgid "Additional information"
msgid "Additional order information"
msgstr "Επιπλέον πληροφορίες"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid "Delete order"
msgstr "Διαγραφή παραγγελίας"
@@ -25414,6 +25567,25 @@ 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"
@@ -25734,27 +25906,10 @@ msgstr "Είσοδος: %(datetime)s"
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 "Αυτή η ερώτηση θα ζητηθεί κατά το check-in."
#: 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
@@ -27238,6 +27393,16 @@ msgstr "Συνδέστε μια συσκευή"
msgid "Hardware model"
msgstr "Μοντέλο υλικού(Hardware model)"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
#, fuzzy, python-format
#| msgid "Begin: %(time)s"
msgid "Last seen: %(time)s"
msgstr "Έναρξη: %(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 "Δεν έχει αρχικοποιηθεί ακόμα"
@@ -30733,12 +30898,6 @@ msgstr "Η σειρά των κατηγοριών έχει ενημερωθεί.
msgid "Not all objects have been selected."
msgstr "Η ερώτηση έχει διαγραφεί."
#: pretix/control/views/item.py
#, fuzzy
#| msgid "Street address"
msgid "Street"
msgstr "Διεύθυνση"
#: pretix/control/views/item.py
msgid "The requested question does not exist."
msgstr "Η ζητούμενη ερώτηση δεν υπάρχει."
@@ -30905,7 +31064,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 ""
@@ -31393,7 +31552,8 @@ msgstr "Θέμα: {subject}"
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."
@@ -33892,6 +34052,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 the original 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 "Customer actions"
@@ -34171,16 +34354,13 @@ msgid "Please select how you want to pay."
msgstr "Επιλέξτε τον τρόπο πληρωμής."
#: 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 ""
"Η προσπάθειά μας να εκτελέσουμε την πληρωμή σας μέσω του PayPal απέτυχε. "
"Δοκιμάστε ξανά ή επικοινωνήστε μαζί μας."
#: pretix/plugins/paypal2/views.py
#, fuzzy
@@ -37551,10 +37731,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 ""
@@ -38050,6 +38226,10 @@ msgstr "Συνολική αξία (χωρίς φόρους)"
msgid "Set price in %(currency)s for %(item)s"
msgstr "Τροποποιήστε την τιμή για %(item)s"
#: 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"
@@ -40023,6 +40203,17 @@ msgstr "Πρόσβαση για εγγραφή"
msgid "Kosovo"
msgstr ""
#, 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 ""
#~ "Η προσπάθειά μας να εκτελέσουμε την πληρωμή σας μέσω του PayPal απέτυχε. "
#~ "Δοκιμάστε ξανά ή επικοινωνήστε μαζί μας."
#~ msgid "All actions"
#~ msgstr "Όλες οι ενέργειες"
@@ -40153,11 +40344,6 @@ msgstr ""
#~ msgid "Please do not use special characters in names."
#~ msgstr "Παρακαλώ εισάγετε το όνομά σας."
#, fuzzy
#~| msgid "Status"
#~ msgid "State"
#~ msgstr "Κατάσταση"
#, fuzzy
#~| msgid "Printing date and time"
#~ msgid "Program times: date and time"
@@ -40747,9 +40933,6 @@ msgstr ""
#~ msgid "Badge design"
#~ msgstr "Σχέδιο badge"
#~ msgid "(Same as above)"
#~ msgstr "(Ιδια όπως παραπάνω)"
#~ msgid "Translation in development"
#~ msgstr "Μετάφραση σε εξέλιξη"
+210 -53
View File
@@ -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 15:35+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -89,6 +89,10 @@ msgstr ""
msgid "Hebrew"
msgstr ""
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr ""
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr ""
@@ -408,6 +412,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 ""
@@ -836,8 +844,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
@@ -854,7 +861,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 ""
@@ -1585,9 +1591,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
@@ -1599,8 +1604,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 ""
@@ -1608,8 +1612,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 ""
@@ -1632,8 +1635,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"
@@ -1661,8 +1663,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"
@@ -2449,7 +2450,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
@@ -2954,13 +2956,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
@@ -2993,6 +2993,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 "
@@ -5319,6 +5329,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 ""
@@ -5356,6 +5370,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 "
@@ -5955,10 +5973,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 ""
@@ -6867,7 +6881,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
@@ -7594,6 +7608,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 ""
@@ -7618,6 +7640,26 @@ 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr ""
@@ -8703,7 +8745,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 ""
@@ -9284,6 +9326,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 ""
@@ -12425,6 +12486,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 ""
@@ -18575,6 +18652,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 ""
@@ -20226,6 +20304,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 ""
@@ -20242,6 +20338,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
@@ -20764,6 +20880,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 ""
@@ -20791,6 +20914,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"
@@ -21046,25 +21186,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
@@ -22302,6 +22427,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 ""
@@ -25177,10 +25311,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 ""
@@ -25326,7 +25456,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 ""
@@ -25716,7 +25846,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."
@@ -27758,6 +27889,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 the original 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 ""
@@ -27983,8 +28137,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
@@ -30717,10 +30874,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 ""
@@ -31118,6 +31271,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"
+309 -188
View File
@@ -7,8 +7,8 @@ msgid ""
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-08 16:00+0000\n"
"POT-Creation-Date: 2026-08-24 15:35+0000\n"
"PO-Revision-Date: 2026-08-25 00:00+0000\n"
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix/"
"es/>\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"
@@ -91,6 +91,10 @@ msgstr "Griego"
msgid "Hebrew"
msgstr "Hebreo"
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr "Húngaro"
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr "Indonesio"
@@ -434,6 +438,10 @@ msgstr "Medio conectado a otro evento"
msgid "You cannot exchange a medium for a medium."
msgstr "No se puede cambiar un medio por otro."
#: pretix/api/views/checkin.py
msgid "You cannot simulate a medium exchange."
msgstr "No se puede simular un intercambio de medio."
#: pretix/api/views/oauth.py pretix/control/logdisplay.py
#, python-brace-format
msgid ""
@@ -888,8 +896,7 @@ msgstr "Información sobre el evento o la fecha"
#: 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 +913,6 @@ msgstr "Participante"
#: 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 "Correo electrónico del participante"
@@ -1393,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
@@ -1413,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"
@@ -1429,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"
@@ -1663,9 +1663,8 @@ msgid "Invoice sender:"
msgstr "Remitente de la factura:"
#: 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
@@ -1677,8 +1676,7 @@ msgstr "Dirección"
#: 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 "Código postal"
@@ -1686,8 +1684,7 @@ msgstr "Código 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 "Ciudad"
@@ -1710,8 +1707,7 @@ msgstr "Estado"
#: 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"
@@ -1739,8 +1735,7 @@ msgstr "Destinatario de la factura:"
#: 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"
@@ -2555,7 +2550,8 @@ msgstr "Estado de pagos"
msgid "Refund states"
msgstr "Estado del reembolso"
#: 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
@@ -3083,16 +3079,12 @@ msgstr ""
"corrupta."
#: pretix/base/forms/questions.py
msgid ""
"If you keep this empty, the ticket will be valid starting at the time of "
"purchase."
msgstr ""
"Si mantienes este campo vacío, la entrada será válida empezando en el "
"momento de la compra."
msgid "Street"
msgstr "Dirección"
#: pretix/base/forms/questions.py
msgid "Street and Number"
msgstr "Calle y número"
msgid "State"
msgstr "Estado o provincia"
#: pretix/base/forms/questions.py
#, python-brace-format
@@ -3124,6 +3116,18 @@ msgstr "Introduzca una fecha y hora no anteriores a {min}."
msgid "Please enter a date and time no later than {max}."
msgstr "Introduzca una fecha y hora que no sea posterior 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 ""
"Si mantienes este campo vacío, la entrada será válida empezando en el "
"momento de la compra."
#: pretix/base/forms/questions.py
msgid "Street and Number"
msgstr "Calle y número"
#: pretix/base/forms/questions.py
msgid ""
"Optional, but depending on the country you reside in we might need to charge "
@@ -4675,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 lets 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
@@ -5768,6 +5769,10 @@ msgstr "La variación escogida no pertenece a este artículo."
msgid "The count needs to be equal to or greater than zero."
msgstr "Se requiere que la cantidad sea igual o mayor que cero."
#: pretix/base/models/items.py pretix/base/models/orders.py
msgid "Order position"
msgstr "Posición del pedido"
#: pretix/base/models/items.py
msgid "Number"
msgstr "Número"
@@ -5805,6 +5810,10 @@ msgstr "Fecha y hora"
msgid "Country code (ISO 3166-1 alpha-2)"
msgstr "Código de país (ISO 3166-1 alfa-2)"
#: pretix/base/models/items.py
msgid "Asked on"
msgstr "Preguntado el"
#: pretix/base/models/items.py pretix/base/models/organizer.py
msgid ""
"The identifier may only contain letters, numbers, dots, dashes, and "
@@ -6463,10 +6472,6 @@ msgstr "Tarjeta de regalo"
msgid "Value"
msgstr "Valor"
#: pretix/base/models/orders.py
msgid "Order position"
msgstr "Posición del pedido"
#: pretix/base/models/orders.py
msgid "Cart ID (e.g. session key)"
msgstr "ID de carrito (p. ej. clave de sesión)"
@@ -7508,7 +7513,7 @@ msgstr "El pago de esta factura ya se ha recibido."
#: 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 "Este pago ya se está procesando y ya no se puede cancelar."
#: pretix/base/payment.py
@@ -7633,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)"
@@ -8282,6 +8284,14 @@ msgstr "Inicio de la preventa"
msgid "Presale end"
msgstr "Fin de la preventa"
#: pretix/base/reldate.py
msgid "Order creation"
msgstr "Creación de pedidos"
#: pretix/base/reldate.py
msgid "Order expiry"
msgstr "Caducidad del pedido"
#: pretix/base/reldate.py
msgid "before"
msgstr "antes"
@@ -8306,6 +8316,26 @@ msgstr "Tiempo relativo:"
msgid "Not set"
msgstr "No fijado"
#: pretix/base/reldate.py
#, python-brace-format
msgid "A relative date cannot be expressed as \"before\" for \"{}\""
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 "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 "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 "Un tiempo relativo no puede expresarse como «después de» para «{}»"
#: pretix/base/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr ""
@@ -9546,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
@@ -9600,7 +9626,7 @@ msgid "Please transfer money to this bank account: 9999-9999-9999-9999"
msgstr ""
"Por favor, transfiera dinero a esta cuenta bancaria: 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 "Este valor se reemplazará en base a parámetros dinámicos."
@@ -10332,6 +10358,30 @@ msgstr ""
"período de ventas designado para este evento. Puede usarlo para describir "
"otras opciones para obtener una entrada, por ejemplo en una taquilla física."
#: pretix/base/settings.py
msgid "No dates match your criteria."
msgstr "No hay fechas que se ajusten a tus criterios."
#: pretix/base/settings.py
msgctxt "subevents"
msgid "Text for empty date results"
msgstr "Texto para los resultados con fechas vacías"
#: 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 ""
"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"
msgstr "Texto guía"
@@ -14289,6 +14339,22 @@ msgstr ""
"Ha configurado las tarjetas de regalo para que sean válidas {} años más el "
"año en el que se emite la tarjeta de regalo."
#: pretix/control/forms/event.py
msgid "Payment term"
msgstr "Condiciones de pago"
#: pretix/control/forms/event.py
msgid "same as above"
msgstr "igual que arriba"
#: pretix/control/forms/event.py
msgid "different payment term in days"
msgstr "plazo de pago diferente en días"
#: pretix/control/forms/event.py
msgid "different payment term in minutes"
msgstr "plazo de pago diferente en minutos"
#: pretix/control/forms/event.py
msgid "Prices including tax"
msgstr "Precios con impuestos incluidos"
@@ -15042,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"
@@ -15302,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"
@@ -15316,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"
@@ -16847,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"
@@ -16888,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"
@@ -20354,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"
@@ -21239,6 +21288,7 @@ msgid "Top recommendation"
msgstr "Recomendación top"
#: 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 "Funcionalidad experimental"
@@ -22091,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
@@ -23166,6 +23214,26 @@ msgstr ""
"entrada. Si usted proporciona alimentos, un ejemplo podría ser preguntar a "
"sus usuarios acerca de las necesidades dietéticas."
#: pretix/control/templates/pretixcontrol/items/questions.html
msgid "Create a new per-ticket question"
msgstr "Crear una nueva pregunta por ticket"
#: pretix/control/templates/pretixcontrol/items/questions.html
msgid "Create a new order-level question"
msgstr "Crear una nueva pregunta a nivel de pedido"
#: pretix/control/templates/pretixcontrol/items/questions.html
msgid "Per-ticket questions"
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"
msgstr "Crear una nueva pregunta"
@@ -23182,6 +23250,30 @@ msgstr "Preguntar durante el check-in"
msgid "All personalized products"
msgstr "Todos los productos personalizados"
#: pretix/control/templates/pretixcontrol/items/questions.html
msgid "Per-order questions"
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
msgid "These questions are asked once per order."
msgstr "Estas preguntas se formulan una vez por pedido."
#: pretix/control/templates/pretixcontrol/items/quota.html
#: pretix/control/templates/pretixcontrol/items/quota_edit.html
#, python-format
@@ -23712,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 ""
@@ -23783,6 +23877,13 @@ msgstr "Datos de la factura"
msgid "(optional)"
msgstr "(opcional)"
#: 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 "Información adicional sobre el pedido"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid "Delete order"
msgstr "Eliminar pedido"
@@ -23812,6 +23913,25 @@ msgstr "Sí, denegar pedido"
msgid "Extend payment term"
msgstr "Ampliar la condición de pago"
#: 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 ""
"Este archivo ha sido cargado por un usuario y puede contener virus u otros "
"contenidos maliciosos."
#: pretix/control/templates/pretixcontrol/order/fragment_question_answer.html
msgid "UNSAFE"
msgstr "INSEGURO"
#: 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 "sin respuesta"
#: pretix/control/templates/pretixcontrol/order/index.html
#, python-format
msgid "Order details: %(code)s"
@@ -24092,27 +24212,10 @@ msgstr "%(datetime_range)s válidos"
msgid "Ticket page"
msgstr "Página de entrada"
#: pretix/control/templates/pretixcontrol/order/index.html
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html
msgid "not answered"
msgstr "sin respuesta"
#: pretix/control/templates/pretixcontrol/order/index.html
msgid "This question will be asked during check-in."
msgstr "Esta pregunta se hará durante el check-in."
#: 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 ""
"Este archivo ha sido cargado por un usuario y puede contener virus u otros "
"contenidos maliciosos."
#: pretix/control/templates/pretixcontrol/order/index.html
msgid "UNSAFE"
msgstr "INSEGURO"
#: pretix/control/templates/pretixcontrol/order/index.html
#: pretix/control/templates/pretixcontrol/orders/overview.html
#: pretix/plugins/reports/accountingreport.py
@@ -25480,6 +25583,15 @@ msgstr "Conectar un dispositivo"
msgid "Hardware model"
msgstr "Modelo del Hardware"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
#, python-format
msgid "Last seen: %(time)s"
msgstr "Última conexión: %(time)s"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
msgid "No recent contact"
msgstr "No ha habido contacto reciente"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
msgid "Not yet initialized"
msgstr "Todavía no inicializado"
@@ -27827,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
@@ -28241,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
@@ -28774,10 +28887,6 @@ msgstr "El orden de las categorías ha sido actualizado."
msgid "Not all objects have been selected."
msgstr "No se han seleccionado todos los objetos."
#: pretix/control/views/item.py
msgid "Street"
msgstr "Dirección"
#: pretix/control/views/item.py
msgid "The requested question does not exist."
msgstr "La pregunta solicitada no existe."
@@ -28928,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 "
@@ -28942,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 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 ""
"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."
@@ -29438,7 +29522,8 @@ msgstr "Asunto: {subject}"
msgid "Your message has been queued and will be sent to {}."
msgstr "Su mensaje ha sido puesto en cola y será enviado 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
msgid ""
"This link is no longer valid. Please go back, refresh the page, and try "
"again."
@@ -31737,6 +31822,38 @@ msgstr ""
"determinado) y pagar en múltiples cuotas o en un plazo de 30 días. Usted, "
"como comerciante, recibirá su dinero de inmediato."
#: 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 ""
"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."
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 "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 --"
msgstr "-- Automático --"
@@ -31998,11 +32115,17 @@ msgstr "Por favor, seleccione cómo desea pagar."
#: 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 ""
"Nuestro intento de ejecutar su pago a través de PayPal ha fallado. Por favor "
"inténtelo de nuevo o contáctenos."
"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 ""
@@ -32254,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."
@@ -35072,10 +35192,6 @@ msgstr[1] "Esta entrada se ha utilizado %(count)s veces."
msgid "No attendee name provided"
msgstr "No se facilitó el nombre del asistente"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html
msgid "The image you previously uploaded"
msgstr "La imagen que subiste anteriormente"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html
msgid "The price of this product was reduced because of an automatic discount."
msgstr "El precio de este producto se redujo debido a un descuento automático."
@@ -35508,6 +35624,10 @@ msgstr "%(value)s sin impuestos"
msgid "Set price in %(currency)s for %(item)s"
msgstr "Establecer precio en %(currency)s para %(item)s"
#: pretix/presale/templates/pretixpresale/event/fragment_question_answer.html
msgid "The image you previously uploaded"
msgstr "La imagen que subiste anteriormente"
#: pretix/presale/templates/pretixpresale/event/fragment_quota_left.html
#, python-format
msgid "%(num)s currently available"
@@ -37285,6 +37405,13 @@ msgstr "Acceso de escritura"
msgid "Kosovo"
msgstr "Kosovo"
#~ msgid ""
#~ "Our attempt to execute your payment via PayPal has failed. Please try "
#~ "again or contact us."
#~ msgstr ""
#~ "Nuestro intento de ejecutar su pago a través de PayPal ha fallado. Por "
#~ "favor inténtelo de nuevo o contáctenos."
#~ msgid "All actions"
#~ msgstr "Todas las acciones"
@@ -37425,9 +37552,6 @@ msgstr "Kosovo"
#~ msgid "Please do not use special characters in names."
#~ msgstr "Por favor, no use caracteres especiales en los nombres."
#~ msgid "State"
#~ msgstr "Estado o provincia"
#~ msgid "Program times: date and time"
#~ msgstr "Horarios del programa: fecha y hora"
@@ -38282,9 +38406,6 @@ msgstr "Kosovo"
#~ msgid "Badge design"
#~ msgstr "Diseño de la insignia"
#~ msgid "(Same as above)"
#~ msgstr "(Lo mismo que arriba)"
#~ msgid "Translation in development"
#~ msgstr "Traducción en desarrollo"
+225 -54
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-30 13:52+0000\n"
"POT-Creation-Date: 2026-08-24 15:35+0000\n"
"PO-Revision-Date: 2025-07-18 01:00+0000\n"
"Last-Translator: Zona Vip <contacto@zonavip.mx>\n"
"Language-Team: Spanish (Latin America) <https://translate.pretix.eu/projects/"
@@ -91,6 +91,10 @@ msgstr "Griego"
msgid "Hebrew"
msgstr "Hebreo"
#: pretix/_base_settings.py
msgid "Hungarian"
msgstr ""
#: pretix/_base_settings.py
msgid "Indonesian"
msgstr "Indonesio"
@@ -437,6 +441,10 @@ msgstr "Medio conectado a otro evento"
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 ""
@@ -902,8 +910,7 @@ msgstr "La información del pedido cambió"
#: 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
@@ -920,7 +927,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 ""
@@ -1704,9 +1710,8 @@ msgid "Invoice sender:"
msgstr "Remitente de la factura:"
#: 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
@@ -1718,8 +1723,7 @@ msgstr "Dirección"
#: 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 "Código Postal"
@@ -1727,8 +1731,7 @@ msgstr "Código 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 "Ciudad"
@@ -1751,8 +1754,7 @@ msgstr "Estado"
#: 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"
@@ -1780,8 +1782,7 @@ msgstr "Destinatario de la factura:"
#: 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"
@@ -2586,7 +2587,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
@@ -3093,14 +3095,15 @@ 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"
msgstr ""
#, fuzzy
#| msgctxt "address"
#| msgid "State"
msgid "State"
msgstr "Estado"
#: pretix/base/forms/questions.py
#, python-brace-format
@@ -3132,6 +3135,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 "
@@ -5481,6 +5494,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 ""
@@ -5518,6 +5535,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 "
@@ -6121,10 +6142,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 ""
@@ -7035,7 +7052,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
@@ -7781,6 +7798,18 @@ msgstr ""
msgid "Presale end"
msgstr ""
#: pretix/base/reldate.py
#, fuzzy
#| msgid "Order total"
msgid "Order creation"
msgstr "Pedido total"
#: pretix/base/reldate.py
#, fuzzy
#| msgid "Order expired"
msgid "Order expiry"
msgstr "Orden caducada"
#: pretix/base/reldate.py
msgid "before"
msgstr ""
@@ -7805,6 +7834,26 @@ 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/secrets.py
msgid "Random (default, works with all pretix apps)"
msgstr ""
@@ -8894,7 +8943,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 ""
@@ -9475,6 +9524,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 ""
@@ -12631,6 +12699,24 @@ msgid ""
"card is issued in."
msgstr ""
#: pretix/control/forms/event.py
#, fuzzy
#| msgid "Payment provider"
msgid "Payment term"
msgstr "Proveedor de pago"
#: 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 ""
@@ -18824,6 +18910,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 ""
@@ -20477,6 +20564,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 ""
@@ -20493,6 +20598,28 @@ msgstr ""
msgid "All personalized products"
msgstr ""
#: pretix/control/templates/pretixcontrol/items/questions.html
#, fuzzy
#| msgid "Order positions"
msgid "Per-order questions"
msgstr "Posiciones de 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 ""
#: 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
@@ -21017,6 +21144,15 @@ 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
#, fuzzy
#| msgid "Order information changed"
msgid "Additional order information"
msgstr "La información del pedido cambió"
#: pretix/control/templates/pretixcontrol/order/delete.html
msgid "Delete order"
msgstr ""
@@ -21044,6 +21180,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"
@@ -21299,25 +21452,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
@@ -22555,6 +22693,16 @@ msgstr ""
msgid "Hardware model"
msgstr ""
#: pretix/control/templates/pretixcontrol/organizers/devices.html
#, fuzzy, python-format
#| msgid "Item name"
msgid "Last seen: %(time)s"
msgstr "Nombre del artículo"
#: pretix/control/templates/pretixcontrol/organizers/devices.html
msgid "No recent contact"
msgstr ""
#: pretix/control/templates/pretixcontrol/organizers/devices.html
msgid "Not yet initialized"
msgstr ""
@@ -25451,10 +25599,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 ""
@@ -25600,7 +25744,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 ""
@@ -25992,7 +26136,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."
@@ -28045,6 +28190,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 the original 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 ""
@@ -28270,8 +28438,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
@@ -31005,10 +31176,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 ""
@@ -31406,6 +31573,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"

Some files were not shown because too many files have changed in this diff Show More