Compare commits

...

97 Commits

Author SHA1 Message Date
Raphael Michel 1226de8d2f Fix isort 2024-07-26 16:43:12 +02:00
Raphael Michel c0d7630eae Fix naming 2024-07-25 13:19:00 +02:00
Raphael Michel 8fe136b374 API: Allow to filter enabled webhooks (Z#23160605) 2024-07-25 10:48:56 +02:00
dependabot[bot] f055a598ce Bump django-compressor from 4.5 to 4.5.1 (#4330)
Bumps [django-compressor](https://github.com/django-compressor/django-compressor) from 4.5 to 4.5.1.
- [Changelog](https://github.com/django-compressor/django-compressor/blob/develop/docs/changelog.txt)
- [Commits](https://github.com/django-compressor/django-compressor/compare/4.5...4.5.1)

---
updated-dependencies:
- dependency-name: django-compressor
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-24 14:38:54 +02:00
Raphael Michel 9138464896 Translations: Delete Abron 2024-07-24 14:38:38 +02:00
Martin Gross 479f51a84c Apply suggestions from code review
Co-authored-by: robbi5 <richt@rami.io>
Co-authored-by: Felix Rindt <felix@rindt.me>
2024-07-24 14:23:12 +02:00
Martin Gross a3ac54d419 Docs: Add documentation on GetYourGuide 2024-07-24 14:23:12 +02:00
Raphael Michel b2841e5c61 SSO Providers: Use redacted field for secret key 2024-07-23 16:26:37 +02:00
dependabot[bot] 3009f50d51 Update pytest requirement from ==8.2.* to ==8.3.* (#4324)
Updates the requirements on [pytest](https://github.com/pytest-dev/pytest) to permit the latest version.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.2.0.dev0...8.3.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-23 15:54:51 +02:00
Gero Nagel ff3a49ab2a Docs: Remove redundant package dependency (#4327)
* remove naming python3-dev twice

* Revert "ListExporter: Do not create excel sheets with more than 30 characters"

This reverts commit ca3802da90.

* Revert "remove naming python3-dev twice"

This reverts commit 7adf2d65e89e4afd82a198e5c0a7416ed8d44b3b.

* Revert "Revert "ListExporter: Do not create excel sheets with more than 30 characters""

This reverts commit d289dc0d1dbf5831452799995a2264642c284c5d.

* delete naming pyhton3-dev twice
2024-07-23 15:54:37 +02:00
Raphael Michel 19f3fbc7e8 Order data export: Include ID of parent position of add-ons 2024-07-23 15:52:55 +02:00
dependabot[bot] bb9b9ac9aa Update pypdf requirement from ==4.2.* to ==4.3.* (#4307)
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/4.2.0...4.3.0)

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

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>
2024-07-23 13:48:11 +02:00
dependabot[bot] d7f6befb5b Bump @babel/preset-env from 7.24.6 to 7.24.7 in /src/pretix/static/npm_dir (#4281)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.24.6 to 7.24.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.7/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-23 13:30:58 +02:00
dependabot[bot] 2287be2009 Update sphinx requirement from ==7.3.* to ==7.4.* (#4306)
Updates the requirements on [sphinx](https://github.com/sphinx-doc/sphinx) to permit the latest version.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES.rst)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v7.3.0...v7.4.4)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-23 13:29:18 +02:00
dependabot[bot] 0480b6873d Update sentry-sdk requirement from ==2.5.* to ==2.10.* (#4305)
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.5.0...2.10.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-23 13:29:11 +02:00
dependabot[bot] 711f08c9e8 Update python-bidi requirement from ==0.4.* to ==0.5.* (#4325)
* Update python-bidi requirement from ==0.4.* to ==0.5.*

Updates the requirements on [python-bidi](https://github.com/MeirKriheli/python-bidi) to permit the latest version.
- [Release notes](https://github.com/MeirKriheli/python-bidi/releases)
- [Changelog](https://github.com/MeirKriheli/python-bidi/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/MeirKriheli/python-bidi/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: python-bidi
  dependency-type: direct:production
...

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

* Update import

---------

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>
2024-07-23 13:27:15 +02:00
Alberto Ortega d18914fcca Translations: Update Spanish
Currently translated at 88.5% (5041 of 5690 strings)

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

powered by weblate
2024-07-23 13:27:07 +02:00
Raphael Michel 2411144262 Ignore a deprecation warning in pypdf 2024-07-23 13:06:00 +02:00
test\"img src=x onerror=prompt(document.cookie) 2f02d35a52 Translations: Add Abron 2024-07-23 11:56:05 +02:00
Richard Schreiber 71e82fda81 Auto-fill questions from invoice address (Z#23143497) (#4303)
* Match invoice address to questions by id, placeholder and label

* make label-text extraction more robust

* match actual label as well
2024-07-23 11:54:10 +02:00
Raphael Michel ca3802da90 ListExporter: Do not create excel sheets with more than 30 characters 2024-07-23 09:35:34 +02:00
JnnisCanis 2c68b9e895 Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (5690 of 5690 strings)

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

powered by weblate
2024-07-22 10:07:01 +02:00
bokor-rami-io 01092498f4 Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (5690 of 5690 strings)

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

powered by weblate
2024-07-22 10:07:01 +02:00
Raphael Michel fd841ed66d Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (5690 of 5690 strings)

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

powered by weblate
2024-07-22 10:07:01 +02:00
Raphael Michel 04cbccb536 Translations: Update German
Currently translated at 100.0% (5690 of 5690 strings)

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

powered by weblate
2024-07-22 10:07:01 +02:00
Raphael Michel b8ea93de1e Fix ticket_download_require_validated_email after sales channel change 2024-07-22 09:38:35 +02:00
Richard Schreiber c49f42301c Fix file-type check for product image 2024-07-19 12:44:49 +02:00
Reece Needham 2ae0a16e67 Translations: Update Spanish
Currently translated at 100.0% (231 of 231 strings)

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

powered by weblate
2024-07-19 11:52:27 +02:00
Reece Needham 6b06fdf822 Translations: Update Spanish
Currently translated at 88.6% (5045 of 5690 strings)

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

powered by weblate
2024-07-19 11:52:27 +02:00
Reece Needham ea3f4e5f62 Translations: Update Spanish
Currently translated at 100.0% (231 of 231 strings)

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

powered by weblate
2024-07-19 11:52:27 +02:00
Reece Needham d71c23f7e0 Translations: Update Spanish
Currently translated at 87.5% (4983 of 5690 strings)

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

powered by weblate
2024-07-19 11:52:27 +02:00
Nikolai 5ed7b0032b Translations: Update Danish
Currently translated at 49.4% (2815 of 5690 strings)

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

powered by weblate
2024-07-19 11:52:27 +02:00
Martin Gross a77f2d01a7 CartManager: Allow to explicitly set an order expiration 2024-07-19 11:38:36 +02:00
Raphael Michel ca4f511cde Voucher import: Fix subevent column 2024-07-19 10:56:17 +02:00
Raphael Michel 83b1c2ea7e Do not take "optional" as part of error message label (#4309) 2024-07-18 14:57:25 +02:00
Richard Schreiber c91eb2e20d Set cursor to not-allowed on labels for disabled checkboxes 2024-07-18 11:54:43 +02:00
Richard Schreiber bfb480a288 UI: in plugin-list only show border-top when necessary (#4314) 2024-07-18 11:43:24 +02:00
Richard Schreiber 22e2143623 API: add api_meta to order 2024-07-18 10:01:03 +02:00
Raphael Michel 9e61f7f978 API: Fix admin permission issue in subevent endpoint 2024-07-18 09:44:30 +02:00
Raphael Michel 092de9e3c4 Add webhook for pretix.event.order.deleted (Z#23159520) (#4310) 2024-07-17 17:15:35 +02:00
Richard Schreiber f0822d3c27 Docs: add help for HTTP-COOP: same-origin, widget and Paypal (#4311)
* Docs: add help for HTTP-COOP, widget and Paypal

* fix typo Paypal => PayPal

Co-authored-by: Raphael Michel <michel@rami.io>

---------

Co-authored-by: Raphael Michel <michel@rami.io>
2024-07-17 16:12:17 +02:00
dependabot[bot] 6fc47ca3b6 Update django-hijack requirement from ==3.5.* to ==3.6.* (#4290)
Updates the requirements on [django-hijack](https://github.com/django-hijack/django-hijack) to permit the latest version.
- [Release notes](https://github.com/django-hijack/django-hijack/releases)
- [Changelog](https://github.com/django-hijack/django-hijack/blob/master/docs/release-button.png)
- [Commits](https://github.com/django-hijack/django-hijack/compare/3.5.0...3.6.0)

---
updated-dependencies:
- dependency-name: django-hijack
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-17 14:27:42 +02:00
Raphael Michel 3716a686f5 Translations: Add Basque 2024-07-17 14:27:30 +02:00
Erik Löfman 9c0c77958e Translations: Update Swedish
Currently translated at 99.9% (5685 of 5690 strings)

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

powered by weblate
2024-07-17 14:27:30 +02:00
Nikolai 6154a44274 Translations: Update Danish
Currently translated at 47.9% (2727 of 5690 strings)

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

powered by weblate
2024-07-17 14:27:30 +02:00
Erik Löfman d5aff10297 Translations: Update Swedish
Currently translated at 98.5% (5610 of 5690 strings)

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

powered by weblate
2024-07-17 14:27:30 +02:00
Erik Löfman 846e39a652 Translations: Update Swedish
Currently translated at 94.4% (5374 of 5690 strings)

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

powered by weblate
2024-07-17 14:27:30 +02:00
Mira 75d37b2a37 Don't exclude localization PRs from test CI action (#4276)
* Don't exclude localization PRs from test CI action

* Update tests.yml

---------

Co-authored-by: Raphael Michel <michel@rami.io>
2024-07-17 14:23:39 +02:00
Raphael Michel 2a0c3da8c4 Fix N+1 query found by sentry (PRETIXEU-AC2) 2024-07-16 11:25:48 +02:00
Martin Gross fb7f4d1160 Control/Waitlist: Add help_text to waiting_list_limit_per_user (Z#23158537) 2024-07-15 13:39:31 +02:00
Erik Löfman 5c8817f0c3 Translations: Update Swedish
Currently translated at 94.2% (5364 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Erik Löfman 7663bf7994 Translations: Update Swedish
Currently translated at 87.3% (4972 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Erik Löfman ea8f74f8aa Translations: Update Swedish
Currently translated at 87.3% (4970 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Nikolai 7b34701449 Translations: Update Danish
Currently translated at 46.1% (2625 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Dirk-jan mollema 06f4bfea24 Translations: Update Dutch
Currently translated at 86.5% (4925 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Nikolai e2862a98a0 Translations: Update Danish
Currently translated at 45.2% (2572 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Erik Löfman 82f4feadc3 Translations: Update Swedish
Currently translated at 81.8% (4657 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Nikolai 906222c7d3 Translations: Update Danish
Currently translated at 66.2% (153 of 231 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Nikolai 7ce2089ca8 Translations: Update Danish
Currently translated at 44.4% (2532 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Erik Löfman 2133584ed2 Translations: Update Swedish
Currently translated at 75.2% (4280 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Nikolai c3c50d7205 Translations: Update Danish
Currently translated at 41.6% (2369 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Erik Löfman 4f7a41a4b2 Translations: Update Swedish
Currently translated at 71.2% (4055 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Nikolai 93fd79ab33 Translations: Update Danish
Currently translated at 65.8% (152 of 231 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Nikolai 59595c2f10 Translations: Update Danish
Currently translated at 40.9% (2332 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Erik Löfman 91c6d09f0b Translations: Update Swedish
Currently translated at 64.8% (3688 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Nikolai 570a818129 Translations: Update Danish
Currently translated at 35.5% (2025 of 5690 strings)

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

powered by weblate
2024-07-15 09:46:49 +02:00
Martin Gross d63e2ebe2d Chore/flake8: Remove extra blank line 2024-07-12 12:34:39 +02:00
Martin Gross 884c97d62a Stripe/SOFORT: Move SOFORT from iFrame to proper redirect (Z#23158598) 2024-07-12 12:30:37 +02:00
Mira 032e958a00 Fix test cases for checkinlist export (#4301) 2024-07-12 11:58:10 +02:00
Mira 7b16dfefbc New column and sort options for check-in-list export (#4300)
Allow check-in-list export to by sorted by order datetime (Z#23158159)
Add check-in text to export
2024-07-12 10:37:22 +02:00
Richard Schreiber 8a5b13dee9 Waitinglist: show time for subevent in overview 2024-07-09 15:37:51 +02:00
Richard Schreiber d7dde8c23e Fix CSS-color in alert-danger icon 2024-07-09 10:17:20 +02:00
Richard Schreiber 1e2f93fbc5 Waitinglist: add time to subevent dropdown when sending vouchers (#4296) 2024-07-09 09:28:19 +02:00
Richard Schreiber 493fc03686 Fix PayPal CSP img-src 2024-07-09 09:26:46 +02:00
Raphael Michel 8d6d885f6e API: Add various filtering options
* API: Add various filtering options (#23158339)

* fix query-param description

* simplify payment provider qs-filter

---------

Co-authored-by: Richard Schreiber <schreiber@rami.io>
2024-07-08 21:31:54 +02:00
Martin Gross 2aa989c293 PPv2: Do not fail hard on refunds that are based on manually confirmed payments (Fixes: #PRETIXEU-AC1) 2024-07-04 15:12:53 +02:00
dependabot[bot] 06b226f40f Update pillow requirement from ==10.3.* to ==10.4.* (#4280)
Updates the requirements on [pillow](https://github.com/python-pillow/Pillow) to permit the latest version.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/10.3.0...10.4.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-04 14:46:46 +02:00
Raphael Michel 73038b0d97 Fix enforcement of restricted plugins (#4286) 2024-07-03 17:14:03 +02:00
Erik Löfman 4513e31f0d Translations: Update Swedish
Currently translated at 63.0% (3585 of 5690 strings)

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

powered by weblate
2024-07-03 17:05:45 +02:00
Raphael Michel d34175114b Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (5690 of 5690 strings)

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

powered by weblate
2024-07-03 17:05:45 +02:00
Raphael Michel b2c71b47ce Translations: Update German
Currently translated at 100.0% (5690 of 5690 strings)

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

powered by weblate
2024-07-03 17:05:45 +02:00
Raphael Michel a889abc52b Unify terminology choice Ticketshop (German) 2024-07-03 13:41:09 +02:00
Raphael Michel 8c01b2a469 API: Fix crash in creating variations 2024-07-03 12:37:41 +02:00
Raphael Michel 720c7fd7bb Fix crash in event cloning (PRETIXEU-ABX) 2024-07-03 11:52:57 +02:00
Raphael Michel 6ae6eba4de API: Add details of seats (#4282) 2024-07-03 09:48:59 +02:00
Raphael Michel a173e347ea Optimize availability queries 2024-07-02 18:29:44 +02:00
Raphael Michel 94d13e4cdd API: Raise the right validation error (PRETIXEU-ABV) 2024-07-02 17:49:09 +02:00
Raphael Michel e618441231 API: Fix crash expanding variations 2024-07-02 14:44:35 +02:00
Raphael Michel cd57f1f024 API: Fix creation of embedded variations with explicit sales channels 2024-07-02 09:25:19 +02:00
Raphael Michel 075b9c187f Item list: Fix exclusive tax rules 2024-07-02 09:03:57 +02:00
Erik Löfman d9f46cb817 Translations: Update Swedish
Currently translated at 59.2% (3374 of 5690 strings)

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

powered by weblate
2024-07-01 18:24:27 +02:00
Anarion Dunedain 2892d16861 Translations: Update Polish
Currently translated at 100.0% (5690 of 5690 strings)

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

powered by weblate
2024-07-01 18:24:27 +02:00
Nikolai 9128624d68 Translations: Update Danish
Currently translated at 34.8% (1985 of 5690 strings)

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

powered by weblate
2024-07-01 18:24:27 +02:00
Raphael Michel d2cf8f801d Sales channels: Fix update view 2024-07-01 17:50:27 +02:00
Raphael Michel 682d0f886d Fix order change edge case (PRETIXEU-ABH) 2024-07-01 14:39:34 +02:00
Raphael Michel d2cbd41a19 Fix ticket preview (PRETIXEU-ABF) 2024-07-01 14:38:25 +02:00
Raphael Michel 828f4e3168 Fix isort and docs test 2024-07-01 11:46:46 +02:00
95 changed files with 37737 additions and 6298 deletions
-1
View File
@@ -5,7 +5,6 @@ on:
branches: [ master ]
paths-ignore:
- 'doc/**'
- 'src/pretix/locale/**'
pull_request:
branches: [ master ]
paths-ignore:
+1 -1
View File
@@ -65,7 +65,7 @@ Package dependencies
To build and run pretix, you will need the following debian packages::
# apt-get install git build-essential python3-dev python3-venv python3 python3-pip \
python3-dev libxml2-dev libxslt1-dev libffi-dev zlib1g-dev libssl-dev \
libxml2-dev libxslt1-dev libffi-dev zlib1g-dev libssl-dev \
gettext libpq-dev libjpeg-dev libopenjp2-7-dev
Config file
+5
View File
@@ -40,6 +40,11 @@ answers list of objects Answers to user
seat objects The assigned seat (or ``null``)
├ id integer Internal ID of the seat instance
├ name string Human-readable seat name
├ zone_name string Name of the zone the seat is in
├ row_name string Name/number of the row the seat is in
├ row_label string Additional label of the row (or ``null``)
├ seat_number string Number of the seat within the row
├ seat_label string Additional label of the seat (or ``null``)
└ seat_guid string Identifier of the seat within the seating plan
===================================== ========================== =======================================================
+2
View File
@@ -96,6 +96,8 @@ Endpoints
:query integer page: The page number in case of a multi-page result set, default is 1
:query string secret: Only show gift cards with the given secret.
:query string value: Only show gift cards with the given value.
:query boolean expired: Filter for gift cards that are (not) expired.
:query boolean testmode: Filter for gift cards that are (not) in test mode.
:query boolean include_accepted: Also show gift cards issued by other organizers that are accepted by this organizer.
:query string expand: If you pass ``"owner_ticket"``, the respective field will be shown as a nested value instead of just an ID.
+1
View File
@@ -164,6 +164,7 @@ Endpoints
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string search: Filter the list by the value of the variation (substring search).
:query boolean active: If set to ``true`` or ``false``, only items with this value for the field ``active`` will be
returned.
:param organizer: The ``slug`` field of the organizer to fetch
+1
View File
@@ -392,6 +392,7 @@ Endpoints
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string search: Filter the list by internal name or name of the item (substring search).
:query boolean active: If set to ``true`` or ``false``, only items with this value for the field ``active`` will be
returned.
:query integer category: If set to the ID of a category, only items within that category will be returned.
+14 -1
View File
@@ -42,6 +42,8 @@ payment_date date **DEPRECATED AN
payment_provider string **DEPRECATED AND INACCURATE** Payment provider used for this order
total money (string) Total value of this order
comment string Internal comment on this order
api_meta object Meta data for that order. Only available through API, no guarantees
on the content structure. You can use this to save references to your system.
custom_followup_at date Internal date for a custom follow-up action
checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if a ticket
@@ -215,6 +217,11 @@ answers list of objects Answers to user
seat objects The assigned seat. Can be ``null``.
├ id integer Internal ID of the seat instance
├ name string Human-readable seat name
├ zone_name string Name of the zone the seat is in
├ row_name string Name/number of the row the seat is in
├ row_label string Additional label of the row (or ``null``)
├ seat_number string Number of the seat within the row
├ seat_label string Additional label of the seat (or ``null``)
└ seat_guid string Identifier of the seat within the seating plan
pdf_data object Data object required for ticket PDF generation. By default,
this field is missing. It will be added only if you add the
@@ -455,10 +462,13 @@ List of all orders
:query datetime modified_since: Only return orders that have changed since the given date. Be careful: We only
recommend using this in combination with ``testmode=false``, since test mode orders can vanish at any time and
you will not notice it using this method.
:query datetime created_since: Only return orders that have been created since the given date.
:query datetime created_since: Only return orders that have been created since the given date (inclusive).
:query datetime created_before: Only return orders that have been created before the given date (exclusive).
:query integer subevent: Only return orders with a position that contains this subevent ID. *Warning:* Result will also include orders if they contain mixed subevents, and it will even return orders where the subevent is only contained in a canceled position.
:query datetime subevent_after: Only return orders that contain a ticket for a subevent taking place after the given date. This is an exclusive after, and it considers the **end** of the subevent (or its start, if the end is not set).
:query datetime subevent_before: Only return orders that contain a ticket for a subevent taking place after the given date. This is an exclusive before, and it considers the **start** of the subevent.
:query string sales_channel: Only return orders with the given sales channel identifier (e.g. ``"web"``).
:query string payment_provider: Only return orders that contain a payment using the given payment provider. Note that this also searches for partial incomplete, or failed payments within the order and is not useful to get a sum of payment amounts without further processing.
:query string exclude: Exclude a field from the output, e.g. ``fees`` or ``positions.downloads``. Can be used as a performance optimization. Can be passed multiple times.
:query string include: Include only the given field in the output, e.g. ``fees`` or ``positions.downloads``. Can be used as a performance optimization. Can be passed multiple times. ``include`` is applied before ``exclude``, so ``exclude`` takes precedence.
:param organizer: The ``slug`` field of the organizer to fetch
@@ -554,6 +564,7 @@ Fetching individual orders
"fees": [],
"total": "23.00",
"comment": "",
"api_meta": {},
"custom_followup_at": null,
"checkin_attention": false,
"checkin_text": null,
@@ -734,6 +745,8 @@ Updating order fields
* ``comment``
* ``api_meta``
* ``custom_followup_at``
* ``invoice_address`` (you always need to supply the full object, or ``null`` to delete the current address)
+2
View File
@@ -41,6 +41,7 @@ The following values for ``action_types`` are valid with pretix core:
* ``pretix.event.order.modified``
* ``pretix.event.order.contact.changed``
* ``pretix.event.order.changed.*``
* ``pretix.event.order.deleted`` (can only occur for test mode orders)
* ``pretix.event.order.refund.created``
* ``pretix.event.order.refund.created.externally``
* ``pretix.event.order.refund.requested``
@@ -115,6 +116,7 @@ Endpoints
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query boolean enabled: Only show webhooks that are or are not enabled
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
+2 -2
View File
@@ -35,11 +35,11 @@ Frontend
--------
.. automodule:: pretix.presale.signals
:members: html_head, html_footer, footer_link, global_footer_link, front_page_top, front_page_bottom, front_page_bottom_widget, fee_calculation_for_cart, contact_form_fields, question_form_fields, contact_form_fields_overrides, question_form_fields_overrides, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, render_seating_plan, checkout_flow_steps, position_info, position_info_top, item_description, global_html_head, global_html_footer, global_html_page_header
:members: html_head, html_footer, footer_link, global_footer_link, front_page_top, front_page_bottom, front_page_bottom_widget, fee_calculation_for_cart, contact_form_fields, question_form_fields, contact_form_fields_overrides, question_form_fields_overrides, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, render_seating_plan, checkout_flow_steps, position_info, position_info_top, item_description, global_html_head, global_html_footer, global_html_page_header, seatingframe_html_head
.. automodule:: pretix.presale.signals
:members: order_info, order_info_top, order_meta_from_request
:members: order_info, order_info_top, order_meta_from_request, order_api_meta_from_request
Request flow
""""""""""""
+1 -1
View File
@@ -17,7 +17,7 @@ The project pretix is split into several components. The main components are:
create and manage their events, items, orders and tickets.
**presale**
This is the ticket-shop itself, containing all of the parts visible to the
This is the ticket shop itself, containing all of the parts visible to the
end user. Also called "frontend" in parts of this documentation.
**api**
+105
View File
@@ -0,0 +1,105 @@
GetYourGuide
============
.. note::
The GetYourGuide integration is currently in Beta. Please contact support@pretix.eu to enable the integration
for your pretix.eu organizer account.
Introduction
------------
Using third party aggregators, such als GetYourGuide, event organizers can sell tickets to their events not only on
their own ticket-shop but also on the aggregator's portal. While this service is not for free, it allows event
organizers to reacher a larger audience that would otherwise not have found their way into the organizers webshop.
Using pretix' integration with GetYourGuide, event organizers can profit from an additional sales and revenue channel,
while keeping the effort for setting up and maintaining multiple ticket shops to a minimum.
Preparing your organizer account
--------------------------------
The first step in enabling the GetYourGuide integration, is to setup a corresponding Sales Channel, which will be used
to properly attribute the sales generated. This needs to be done only once per organizer account.
To do so, log into the pretix backend, select ``Organizers`` from the navigation and then the organizer in question.
Extending the ``Settings``-menu, find the ``Sales channels`` configuration and click the ``Add a new channel`` button.
On the following page, you will be able to select ``GetYourGuide`` as the sales channel type and give it a custom name.
Preparing your event
--------------------
In order to now sell your events on GetYourGuide, you will need to configure each event in question.
1. Enabling the plugin
Within your event, extend the ``Settings`` menu and navigate to ``Plugins``. Activate the plugin in the
``Integrations`` tab.
2. Sell the event on the sales channel
Pick the sales channel or channels, on which you would like to sell your event by navigating to the event's general
settings page using the ``Sell on all sales channels`` or ``Restrict to specific sales channels`` checkboxes.
3. Configure one or more products to be sold on GetYourGuide
Either create a new or edit an existing product, that you would like to sell on GetYourGuide. To do so, you will
need to have checked the ``Sell on all sales channels`` or appropriate ``Restrict to specific sales channels``
checkbox of the product within it's ``Availability`` tab.
In addition, you will also need to set the GetYourGuide equivalent ticket category in the product's accordingly
named settings tab. Within your event, there can be only one product per ticket category. Depending on your further
configuration, you must at least select one product to be in the ``Adult`` or ``Group`` category.
4. Configuring the GetYourGuide-plugin
Once you have configured one or more products to be eligible to be sold on GetYourGuide, you'll need to configure a
few basic settings within the event (``Settings`` --> ``GetYourGuide``). The most important settings can be found
the in the ``Configuration`` tab, such as the location of the event on sale.
Ticket Categories
-----------------
While pretix only uses the ticket category term loosely to group together multiple products for nicer display,
GetYourGuide is relying on the ticket categories to price the tickets.
First of all, you need to make the decision on how you are planning on selling your tickets on GetYourGuide - in most
cases, this will reflect your current sales strategy within your pretix shop.
- Individual tickets
Every single person attending will need to purchase their own ticket. A family of two adults and two
children will have to purchase and pay for a total of 4 tickets.
In this case, you will need to offer *at least* a ticket of the ``Adult`` type, but may offer any other ticket
category type (Child, Youth, Senior, ...) in addition. But you cannot offer a ``Group`` ticket.
- Group tickets
Two groups, consisting of 10 and 20 participants respectively, won't need to purchase a total of 30 tickets, but
rather two group tickets. It is up to you to configure the group size limits within the GetYourGuide-settings of your
product.
Choosing this option, you cannot offer any other ticket categories besides ``Group``.
Setting up event dates and quotas
---------------------------------
Of course, in addition to creating products, you will also need to add them to a quota for them to be available for
sale. The process for doing this is the very same as for any regular event or event series.
.. note::
When selling individual tickets through GetYourGuide, you will not be able to offer differing quantities for
individual ticket categories.
For this reason, we recommend to place all GetYourGuide-eligible products into the same quota. Should you however opt
to create multiple quotas which create an imbalance, pretix will report only the available number of tickets for the
lowest relevant quota.
Connecting your event to GetYourGuide
-------------------------------------
Once you have set up your event and products and performed all necessary configuration, you may want to use the
Analyzer-feature of our GetYourGuide-plugin (``Settings`` -> ``GetYourGuide`` -> tab ``Analyzer``).
The Analyzer should not display any blocking error messages and at least one event date that is ready for publishing on
the GetYourGuide platform.
At this point, you will need to setup your event (called ``product`` in the GetYourGuide universe) on their
`Supplier Portal`_ and connect it with your pretix shop. To do so, please follow the
`Connecting a new product to your Reservation System`_ on the GetYourGuide Supply Partner Help Center.
Select ``pretix.eu`` as your reservation system; the required ``product ID`` can be found in the ``Configuration`` tab
of the GetYourGuide plugin settings page.
From this point on, GetYourGuide will automatically import the availabilities and products and offer them for sale.
.. _Supplier Portal: https://suppliers.getyourguide.com/
.. _Connecting a new product to your Reservation System: https://supply.getyourguide.support/hc/en-us/articles/18008029689373-Connecting-a-new-product-to-your-Reservation-system
+1
View File
@@ -25,3 +25,4 @@ If you want to **create** a plugin, please go to the
webinar
presale-saml
kulturpass
getyourguide
+1 -1
View File
@@ -1,4 +1,4 @@
sphinx==7.3.*
sphinx==7.4.*
jinja2==3.1.*
sphinx-rtd-theme
sphinxcontrib-httpdomain
+1 -1
View File
@@ -1,5 +1,5 @@
-e ../
sphinx==7.3.*
sphinx==7.4.*
jinja2==3.1.*
sphinx-rtd-theme
sphinxcontrib-httpdomain
+10
View File
@@ -449,6 +449,16 @@ Further reading:
* `Stripe Payment Method Domain registration`_
External payment providers and Cross-Origin-Opener-Policy
---------------------------------------------------------
If you use a payment provider that opens a new window during checkout (such as PayPal), be aware that setting
``Cross-Origin-Opener-Policy: same-origin`` results in an empty popup-window being opened in the foreground. This is
due to JavaScript not having access to the opened window. To mitigate this, you either need to always open the widgets
checkout in a new tab (see :ref:`Always open a new tab`) or set ``Cross-Origin-Opener-Policy: same-origin-allow-popups``
Working with Cross-Origin-Embedder-Policy
-----------------------------------------
+7 -7
View File
@@ -38,13 +38,13 @@ dependencies = [
"dj-static",
"Django[argon2]==4.2.*",
"django-bootstrap3==24.2",
"django-compressor==4.5",
"django-compressor==4.5.1",
"django-countries==7.6.*",
"django-filter==24.2",
"django-formset-js-improved==0.5.0.3",
"django-formtools==2.5.1",
"django-hierarkey==1.2.*",
"django-hijack==3.5.*",
"django-hijack==3.6.*",
"django-i18nfield==1.9.*,>=1.9.4",
"django-libsass==0.9",
"django-localflavor==4.0",
@@ -75,15 +75,15 @@ dependencies = [
"paypal-checkout-serversdk==1.0.*",
"PyJWT==2.8.*",
"phonenumberslite==8.13.*",
"Pillow==10.3.*",
"Pillow==10.4.*",
"pretix-plugin-build",
"protobuf==5.27.*",
"psycopg2-binary",
"pycountry",
"pycparser==2.22",
"pycryptodome==3.20.*",
"pypdf==4.2.*",
"python-bidi==0.4.*", # Support for Arabic in reportlab
"pypdf==4.3.*",
"python-bidi==0.5.*", # Support for Arabic in reportlab
"python-dateutil==2.9.*",
"pytz",
"pytz-deprecation-shim==0.1.*",
@@ -92,7 +92,7 @@ dependencies = [
"redis==5.0.*",
"reportlab==4.2.*",
"requests==2.31.*",
"sentry-sdk==2.5.*",
"sentry-sdk==2.10.*",
"sepaxml==2.6.*",
"slimit",
"static3==0.7.*",
@@ -127,7 +127,7 @@ dev = [
"pytest-rerunfailures==14.*",
"pytest-sugar",
"pytest-xdist==3.6.*",
"pytest==8.2.*",
"pytest==8.3.*",
"responses",
]
+4 -1
View File
@@ -21,8 +21,9 @@
#
import json
from django.core.exceptions import ValidationError
from django.db.models import prefetch_related_objects
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class AsymmetricField(serializers.Field):
@@ -80,6 +81,7 @@ class SalesChannelMigrationMixin:
def to_internal_value(self, data):
if "sales_channels" in data:
prefetch_related_objects([self.organizer], "sales_channels")
all_channels = {
s.identifier for s in
self.organizer.sales_channels.all()
@@ -109,6 +111,7 @@ class SalesChannelMigrationMixin:
def to_representation(self, value):
value = super().to_representation(value)
if value.get("all_sales_channels"):
prefetch_related_objects([self.organizer], "sales_channels")
value["sales_channels"] = sorted([
s.identifier for s in
self.organizer.sales_channels.all()
+5 -1
View File
@@ -281,13 +281,17 @@ class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
from pretix.base.plugins import get_all_plugins
plugins_available = {
p.module for p in get_all_plugins(self.instance)
p.module: p for p in get_all_plugins(self.instance)
if not p.name.startswith('.') and getattr(p, 'visible', True)
}
settings_holder = self.instance if self.instance and self.instance.pk else self.context['organizer']
for plugin in value.get('plugins'):
if plugin not in plugins_available:
raise ValidationError(_('Unknown plugin: \'{name}\'.').format(name=plugin))
if getattr(plugins_available[plugin], 'restricted', False):
if plugin not in settings_holder.settings.allowed_restricted_plugins:
raise ValidationError(_('Restricted plugin: \'{name}\'.').format(name=plugin))
return value
+11 -1
View File
@@ -76,7 +76,9 @@ class InlineItemVariationSerializer(SalesChannelMigrationMixin, I18nAwareModelSe
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['require_membership_types'].queryset = lazy(lambda: self.context['event'].organizer.membership_types.all(), QuerySet)
self.fields['limit_sales_channels'].child_relation.queryset = lazy(lambda: self.context['event'].organizer.sales_channels.all(), QuerySet)
self.fields['limit_sales_channels'].child_relation.queryset = (
self.context['event'].organizer.sales_channels.all() if 'event' in self.context else SalesChannel.objects.none()
)
def validate_meta_data(self, value):
for key in value['meta_data'].keys():
@@ -115,11 +117,15 @@ class ItemVariationSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializ
def create(self, validated_data):
meta_data = validated_data.pop('meta_data', None)
require_membership_types = validated_data.pop('require_membership_types', [])
limit_sales_channels = validated_data.pop('limit_sales_channels', [])
variation = ItemVariation.objects.create(**validated_data)
if require_membership_types:
variation.require_membership_types.add(*require_membership_types)
if limit_sales_channels:
variation.limit_sales_channels.add(*limit_sales_channels)
# Meta data
if meta_data is not None:
for key, value in meta_data.items():
@@ -284,6 +290,7 @@ class ItemSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
self.fields['require_membership_types'].queryset = self.context['event'].organizer.membership_types.all()
self.fields['grant_membership_type'].queryset = self.context['event'].organizer.membership_types.all()
self.fields['limit_sales_channels'].child_relation.queryset = self.context['event'].organizer.sales_channels.all()
self.fields['variations'].child.fields['limit_sales_channels'].child_relation.queryset = self.context['event'].organizer.sales_channels.all()
def validate(self, data):
data = super().validate(data)
@@ -371,10 +378,13 @@ class ItemSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
for variation_data in variations_data:
require_membership_types = variation_data.pop('require_membership_types', [])
limit_sales_channels = variation_data.pop('limit_sales_channels', [])
var_meta_data = variation_data.pop('meta_data', {})
v = ItemVariation.objects.create(item=item, **variation_data)
if require_membership_types:
v.require_membership_types.add(*require_membership_types)
if limit_sales_channels:
v.limit_sales_channels.add(*limit_sales_channels)
if var_meta_data is not None:
for key, value in var_meta_data.items():
+5 -5
View File
@@ -165,7 +165,7 @@ class InlineSeatSerializer(I18nAwareModelSerializer):
class Meta:
model = Seat
fields = ('id', 'name', 'seat_guid')
fields = ('id', 'name', 'seat_guid', 'zone_name', 'row_name', 'row_label', 'seat_label', 'seat_number')
class AnswerSerializer(I18nAwareModelSerializer):
@@ -585,7 +585,7 @@ class CheckinListOrderPositionSerializer(OrderPositionSerializer):
self.fields['item'] = ItemSerializer(read_only=True, context=self.context)
if 'variation' in self.context['expand']:
self.fields['variation'] = InlineItemVariationSerializer(read_only=True)
self.fields['variation'] = InlineItemVariationSerializer(read_only=True, context=self.context)
if 'answers.question' in self.context['expand']:
self.fields['answers'].child.fields['question'] = QuestionSerializer(read_only=True)
@@ -726,7 +726,7 @@ class OrderSerializer(I18nAwareModelSerializer):
'code', 'event', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
'checkin_attention', 'checkin_text', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
'url', 'customer', 'valid_if_pending'
'url', 'customer', 'valid_if_pending', 'api_meta'
)
read_only_fields = (
'code', 'status', 'testmode', 'secret', 'datetime', 'expires', 'payment_date',
@@ -786,7 +786,7 @@ class OrderSerializer(I18nAwareModelSerializer):
# Even though all fields that shouldn't be edited are marked as read_only in the serializer
# (hopefully), we'll be extra careful here and be explicit about the model fields we update.
update_fields = ['comment', 'custom_followup_at', 'checkin_attention', 'checkin_text', 'email', 'locale',
'phone', 'valid_if_pending']
'phone', 'valid_if_pending', 'api_meta']
if 'invoice_address' in validated_data:
iadata = validated_data.pop('invoice_address')
@@ -1059,7 +1059,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
fields = ('code', 'status', 'testmode', 'email', 'phone', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
'invoice_address', 'positions', 'checkin_attention', 'checkin_text', 'payment_info', 'payment_date',
'consume_carts', 'force', 'send_email', 'simulate', 'customer', 'custom_followup_at',
'require_approval', 'valid_if_pending', 'expires')
'require_approval', 'valid_if_pending', 'expires', 'api_meta')
def validate_payment_provider(self, pp):
if pp is None:
+1 -1
View File
@@ -406,7 +406,7 @@ def _checkin_list_position_queryset(checkinlists, ignore_status=False, ignore_pr
'item__variations').select_related('item__tax_rule')
if expand and 'variation' in expand:
qs = qs.prefetch_related('variation')
qs = qs.prefetch_related('variation', 'variation__meta_values')
return qs
+16 -3
View File
@@ -41,6 +41,7 @@ from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
from rest_framework import serializers, views, viewsets
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response
from pretix.api.auth.permission import EventCRUDPermission
@@ -162,7 +163,13 @@ class EventViewSet(viewsets.ModelViewSet):
qs = filter_qs_by_attr(qs, self.request)
if 'with_availability_for' in self.request.GET:
qs = Event.annotated(qs, channel=self.request.GET.get('with_availability_for'))
qs = Event.annotated(
qs,
channel=get_object_or_404(
self.request.organizer.sales_channels,
identifier=self.request.GET.get('with_availability_for')
)
)
return qs.prefetch_related(
'organizer',
@@ -436,13 +443,19 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet):
elif self.request.user.is_authenticated:
qs = SubEvent.objects.filter(
event__organizer=self.request.organizer,
event__in=self.request.user.get_events_with_any_permission()
event__in=self.request.user.get_events_with_any_permission(request=self.request)
)
qs = filter_qs_by_attr(qs, self.request)
if 'with_availability_for' in self.request.GET:
qs = SubEvent.annotated(qs, channel=self.request.GET.get('with_availability_for'))
qs = SubEvent.annotated(
qs,
channel=get_object_or_404(
self.request.organizer.sales_channels,
identifier=self.request.GET.get('with_availability_for')
)
)
return qs.prefetch_related(
'event',
+20
View File
@@ -56,10 +56,17 @@ from pretix.base.models import (
)
from pretix.base.services.quotas import QuotaAvailability
from pretix.helpers.dicts import merge_dicts
from pretix.helpers.i18n import i18ncomp
with scopes_disabled():
class ItemFilter(FilterSet):
tax_rate = django_filters.CharFilter(method='tax_rate_qs')
search = django_filters.CharFilter(method='search_qs')
def search_qs(self, queryset, name, value):
return queryset.filter(
Q(internal_name__icontains=value) | Q(name__icontains=i18ncomp(value))
)
def tax_rate_qs(self, queryset, name, value):
if value in ("0", "None", "0.00"):
@@ -71,6 +78,18 @@ with scopes_disabled():
model = Item
fields = ['active', 'category', 'admission', 'tax_rate', 'free_price']
class ItemVariationFilter(FilterSet):
search = django_filters.CharFilter(method='search_qs')
def search_qs(self, queryset, name, value):
return queryset.filter(
Q(value__icontains=i18ncomp(value))
)
class Meta:
model = ItemVariation
fields = ['active']
class ItemViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = ItemSerializer
@@ -140,6 +159,7 @@ class ItemVariationViewSet(viewsets.ModelViewSet):
serializer_class = ItemVariationSerializer
queryset = ItemVariation.objects.none()
filter_backends = (DjangoFilterBackend, TotalOrderingFilter,)
filterset_class = ItemVariationFilter
ordering_fields = ('id', 'position')
ordering = ('id',)
permission = None
+8
View File
@@ -108,6 +108,7 @@ with scopes_disabled():
status = django_filters.CharFilter(field_name='status', lookup_expr='iexact')
modified_since = django_filters.IsoDateTimeFilter(field_name='last_modified', lookup_expr='gte')
created_since = django_filters.IsoDateTimeFilter(field_name='datetime', lookup_expr='gte')
created_before = django_filters.IsoDateTimeFilter(field_name='datetime', lookup_expr='lt')
subevent_after = django_filters.IsoDateTimeFilter(method='subevent_after_qs')
subevent_before = django_filters.IsoDateTimeFilter(method='subevent_before_qs')
search = django_filters.CharFilter(method='search_qs')
@@ -115,6 +116,8 @@ with scopes_disabled():
variation = django_filters.CharFilter(field_name='all_positions', lookup_expr='variation_id', distinct=True)
subevent = django_filters.CharFilter(field_name='all_positions', lookup_expr='subevent_id', distinct=True)
customer = django_filters.CharFilter(field_name='customer__identifier')
sales_channel = django_filters.CharFilter(field_name='sales_channel__identifier')
payment_provider = django_filters.CharFilter(method='provider_qs')
class Meta:
model = Order
@@ -138,6 +141,11 @@ with scopes_disabled():
)
return qs
def provider_qs(self, qs, name, value):
return qs.filter(Exists(
OrderPayment.objects.filter(order=OuterRef('pk'), provider=value)
))
def subevent_before_qs(self, qs, name, value):
if getattr(self.request, 'event', None):
subevents = self.request.event.subevents
+10 -1
View File
@@ -24,10 +24,11 @@ from decimal import Decimal
import django_filters
from django.contrib.auth.hashers import make_password
from django.db import transaction
from django.db.models import OuterRef, Subquery, Sum
from django.db.models import OuterRef, Q, Subquery, Sum
from django.db.models.functions import Coalesce
from django.shortcuts import get_object_or_404
from django.utils.functional import cached_property
from django.utils.timezone import now
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
from rest_framework import mixins, serializers, status, views, viewsets
@@ -136,11 +137,19 @@ class SeatingPlanViewSet(viewsets.ModelViewSet):
with scopes_disabled():
class GiftCardFilter(FilterSet):
secret = django_filters.CharFilter(field_name='secret', lookup_expr='iexact')
expired = django_filters.BooleanFilter(method='expired_qs')
value = django_filters.NumberFilter(field_name='cached_value')
class Meta:
model = GiftCard
fields = ['secret', 'testmode']
def expired_qs(self, qs, name, value):
if value:
return qs.filter(expires__isnull=False, expires__lt=now())
else:
return qs.filter(Q(expires__isnull=True) | Q(expires__gte=now()))
class GiftCardViewSet(viewsets.ModelViewSet):
serializer_class = GiftCardSerializer
+8
View File
@@ -19,6 +19,8 @@
# 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 django_filters
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from rest_framework import viewsets
from pretix.api.models import WebHook
@@ -26,11 +28,17 @@ from pretix.api.serializers.webhooks import WebHookSerializer
from pretix.helpers.dicts import merge_dicts
class WebhookFilter(FilterSet):
enabled = django_filters.rest_framework.BooleanFilter()
class WebHookViewSet(viewsets.ModelViewSet):
serializer_class = WebHookSerializer
queryset = WebHook.objects.none()
permission = 'can_change_organizer_settings'
write_permission = 'can_change_organizer_settings'
filter_backends = (DjangoFilterBackend,)
filterset_class = WebhookFilter
def get_queryset(self):
return self.request.organizer.webhooks.prefetch_related('listeners')
+15
View File
@@ -126,6 +126,17 @@ class ParametrizedOrderWebhookEvent(ParametrizedWebhookEvent):
}
class DeletedOrderWebhookEvent(ParametrizedWebhookEvent):
def build_payload(self, logentry: LogEntry):
return {
'notification_id': logentry.pk,
'organizer': logentry.organizer.slug,
'event': logentry.event.slug,
'code': logentry.parsed_data.get("code"),
'action': logentry.action_type,
}
class ParametrizedEventWebhookEvent(ParametrizedWebhookEvent):
def build_payload(self, logentry: LogEntry):
@@ -297,6 +308,10 @@ def register_default_webhook_events(sender, **kwargs):
'pretix.event.order.denied',
_('Order denied'),
),
DeletedOrderWebhookEvent(
'pretix.event.order.deleted',
_('Order deleted'),
),
ParametrizedOrderPositionCheckinWebhookEvent(
'pretix.event.checkin',
_('Ticket checked in'),
+2 -2
View File
@@ -256,7 +256,7 @@ class ListExporter(BaseExporter):
ws = wb.create_sheet()
self.prepare_xlsx_sheet(ws)
try:
ws.title = str(self.verbose_name)
ws.title = str(self.verbose_name)[:30]
except:
pass
total = 0
@@ -374,7 +374,7 @@ class MultiSheetListExporter(ListExporter):
wb = SafeWorkbook(write_only=True)
n_sheets = len(self.sheets)
for i_sheet, (s, l) in enumerate(self.sheets):
ws = wb.create_sheet(str(l))
ws = wb.create_sheet(str(l)[:30])
if hasattr(self, 'prepare_xlsx_sheet_' + s):
getattr(self, 'prepare_xlsx_sheet_' + s)(ws)
+5 -2
View File
@@ -560,7 +560,7 @@ class OrderListExporter(MultiSheetListExporter):
),
).select_related(
'order', 'order__invoice_address', 'order__customer', 'item', 'variation',
'voucher', 'tax_rule'
'voucher', 'tax_rule', 'addon_to',
).prefetch_related(
'subevent', 'subevent__meta_values',
'answers', 'answers__question', 'answers__options'
@@ -619,6 +619,7 @@ class OrderListExporter(MultiSheetListExporter):
_('Valid until'),
_('Order comment'),
_('Follow-up date'),
_('Add-on to position ID'),
]
questions = list(Question.objects.filter(event__in=self.events))
@@ -652,7 +653,8 @@ class OrderListExporter(MultiSheetListExporter):
_('VAT ID'),
]
headers += [
_('Sales channel'), _('Order locale'),
_('Sales channel'),
_('Order locale'),
_('E-mail address verified'),
_('External customer ID'),
_('Check-in lists'),
@@ -743,6 +745,7 @@ class OrderListExporter(MultiSheetListExporter):
]
row.append(order.comment)
row.append(order.custom_followup_at.strftime("%Y-%m-%d") if order.custom_followup_at else "")
row.append(op.addon_to.positionid if op.addon_to_id else "")
acache = {}
for a in op.answers.all():
# We do not want to localize Date, Time and Datetime question answers, as those can lead
+1 -1
View File
@@ -30,7 +30,7 @@ from typing import Tuple
import bleach
import vat_moss.exchange_rates
from bidi.algorithm import get_display
from bidi import get_display
from django.contrib.staticfiles import finders
from django.db.models import Sum
from django.dispatch import receiver
@@ -1,6 +1,7 @@
# Generated by Django 4.2.8 on 2024-07-01 09:26
from django.db import migrations, models
import pretix.base.models.orders
@@ -0,0 +1,18 @@
# Generated by Django 4.2.13 on 2024-07-17 14:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0268_remove_subevent_items_remove_subevent_variations_and_more'),
]
operations = [
migrations.AddField(
model_name='order',
name='api_meta',
field=models.JSONField(default=dict),
),
]
+4 -4
View File
@@ -28,9 +28,9 @@ from django.utils.translation import gettext as _, gettext_lazy, pgettext_lazy
from pretix.base.modelimport import (
BooleanColumnMixin, DatetimeColumnMixin, DecimalColumnMixin, ImportColumn,
IntegerColumnMixin, i18n_flat,
IntegerColumnMixin, SubeventColumnMixin, i18n_flat,
)
from pretix.base.models import ItemVariation, Quota, Seat, Voucher
from pretix.base.models import ItemVariation, Quota, Seat, SubEvent, Voucher
from pretix.base.signals import voucher_import_columns
@@ -55,11 +55,11 @@ class CodeColumn(ImportColumn):
obj.code = value
class SubeventColumn(ImportColumn):
class SubeventColumn(SubeventColumnMixin, ImportColumn):
identifier = 'subevent'
verbose_name = pgettext_lazy('subevents', 'Date')
def assign(self, value, obj: Voucher, **kwargs):
def assign(self, value, obj: SubEvent, **kwargs):
obj.subevent = value
+18 -7
View File
@@ -304,10 +304,13 @@ class EventMixin:
return safe_string(json.dumps(eventdict))
@classmethod
def annotated(cls, qs, channel='web', voucher=None):
from pretix.base.models import Item, ItemVariation, Quota
def annotated(cls, qs, channel, voucher=None):
# Channel can currently be a SalesChannel or a str, since we need that compatibility, but a SalesChannel
# makes the query SIGNIFICANTLY faster
from pretix.base.models import Item, ItemVariation, Quota, SalesChannel
assert isinstance(channel, (SalesChannel, str))
assert isinstance(channel, str)
sq_active_item = Item.objects.using(settings.DATABASE_REPLICA).filter_available(channel=channel, voucher=voucher).filter(
Q(variations__isnull=True)
& Q(quotas__pk=OuterRef('pk'))
@@ -317,18 +320,23 @@ class EventMixin:
q_variation = (
Q(active=True)
& Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel))
& Q(Q(available_from__isnull=True) | Q(available_from__lte=time_machine_now()))
& Q(Q(available_until__isnull=True) | Q(available_until__gte=time_machine_now()))
& Q(item__active=True)
& Q(Q(item__available_from__isnull=True) | Q(item__available_from__lte=time_machine_now()))
& Q(Q(item__available_until__isnull=True) | Q(item__available_until__gte=time_machine_now()))
& Q(Q(item__category__isnull=True) | Q(item__category__is_addon=False))
& Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels__identifier=channel))
& Q(item__require_bundling=False)
& Q(quotas__pk=OuterRef('pk'))
)
if isinstance(channel, str):
q_variation &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel))
q_variation &= Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels__identifier=channel))
else:
q_variation &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels=channel))
q_variation &= Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels=channel))
if voucher:
if voucher.variation_id:
q_variation &= Q(pk=voucher.variation_id)
@@ -962,7 +970,7 @@ class Event(EventMixin, LoggedModel):
):
c_items = list(d.condition_limit_products.all())
b_items = list(d.benefit_limit_products.all())
limit_sales_channels = list(v.limit_sales_channels.all())
limit_sales_channels = list(d.limit_sales_channels.all())
d.pk = None
d.event = self
d._prefetched_objects_cache = {}
@@ -1536,8 +1544,11 @@ class SubEvent(EventMixin, LoggedModel):
return qs_annotated
@classmethod
def annotated(cls, qs, channel='web', voucher=None):
def annotated(cls, qs, channel, voucher=None):
from .items import SubEventItem, SubEventItemVariation
from .organizer import SalesChannel
assert isinstance(channel, (str, SalesChannel))
qs = super().annotated(qs, channel, voucher=voucher)
qs = qs.annotate(
+10 -2
View File
@@ -271,16 +271,24 @@ class SubEventItemVariation(models.Model):
def filter_available(qs, channel='web', voucher=None, allow_addons=False):
assert isinstance(channel, str)
# Channel can currently be a SalesChannel or a str, since we need that compatibility, but a SalesChannel
# makes the query SIGNIFICANTLY faster
from .organizer import SalesChannel
assert isinstance(channel, (SalesChannel, str))
q = (
# IMPORTANT: If this is updated, also update the ItemVariation query
# in models/event.py: EventMixin.annotated()
Q(active=True)
& Q(Q(available_from__isnull=True) | Q(available_from__lte=time_machine_now()) | Q(available_from_mode='info'))
& Q(Q(available_until__isnull=True) | Q(available_until__gte=time_machine_now()) | Q(available_until_mode='info'))
& Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel))
& Q(require_bundling=False)
)
if isinstance(channel, str):
q &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel))
else:
q &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels=channel))
if not allow_addons:
q &= Q(Q(category__isnull=True) | Q(category__is_addon=False))
+5
View File
@@ -299,6 +299,11 @@ class Order(LockModel, LoggedModel):
verbose_name=_("Meta information"),
null=True, blank=True
)
api_meta = models.JSONField(
verbose_name=_("API meta information"),
null=False, blank=True,
default=dict
)
last_modified = models.DateTimeField(
auto_now=True, db_index=False
)
+1 -1
View File
@@ -49,7 +49,7 @@ from io import BytesIO
import jsonschema
import reportlab.rl_config
from bidi.algorithm import get_display
from bidi import get_display
from django.conf import settings
from django.contrib.staticfiles import finders
from django.core.exceptions import ValidationError
+8 -2
View File
@@ -275,7 +275,7 @@ class CartManager:
}
def __init__(self, event: Event, cart_id: str, sales_channel: SalesChannel,
invoice_address: InvoiceAddress=None, widget_data=None):
invoice_address: InvoiceAddress=None, widget_data=None, expiry=None):
self.event = event
self.cart_id = cart_id
self.real_now_dt = now()
@@ -287,6 +287,7 @@ class CartManager:
self._variations_cache = {}
self._seated_cache = {}
self._expiry = None
self._explicit_expiry = expiry
self.invoice_address = invoice_address
self._widget_data = widget_data or {}
self._sales_channel = sales_channel
@@ -305,7 +306,12 @@ class CartManager:
return self._seated_cache[item, subevent]
def _calculate_expiry(self):
self._expiry = self.real_now_dt + timedelta(minutes=self.event.settings.get('reservation_time', as_type=int))
if self._explicit_expiry:
self._expiry = self._explicit_expiry
else:
self._expiry = self.real_now_dt + timedelta(
minutes=self.event.settings.get('reservation_time', as_type=int)
)
def _check_presale_dates(self):
if self.event.presale_start and time_machine_now(self.real_now_dt) < self.event.presale_start:
+9 -6
View File
@@ -960,7 +960,7 @@ def _get_fees(positions: List[CartPosition], payment_requests: List[dict], addre
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):
customer=None, valid_if_pending=False, api_meta: dict=None):
payments = []
try:
@@ -985,6 +985,7 @@ def _create_order(event: Event, *, email: str, positions: List[CartPosition], no
total=total,
testmode=True if sales_channel.type_instance.testmode_supported and event.testmode else False,
meta_info=json.dumps(meta_info or {}),
api_meta=api_meta or {},
require_approval=require_approval,
sales_channel=sales_channel,
customer=customer,
@@ -1096,7 +1097,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):
shown_total=None, customer=None, api_meta: dict=None):
for p in payment_requests:
p['pprov'] = event.get_payment_providers(cached=True)[p['provider']]
if not p['pprov']:
@@ -1200,7 +1201,8 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
sales_channel=sales_channel,
shown_total=shown_total,
customer=customer,
valid_if_pending=valid_if_pending
valid_if_pending=valid_if_pending,
api_meta=api_meta,
)
try:
@@ -2042,7 +2044,7 @@ class OrderChangeManager:
# This also prevents accidental removal through the UI because a hidden product will no longer
# be part of the input.
(a.variation and a.variation.unavailability_reason(has_voucher=True, subevent=a.subevent))
or (a.variation and self.order.sales_channel not in a.variation.sales_channels)
or (a.variation and not a.variation.all_sales_channels and not a.variation.limit_sales_channels.contains(self.order.sales_channel))
or a.item.unavailability_reason(has_voucher=True, subevent=a.subevent)
or (
not item.all_sales_channels and
@@ -2873,12 +2875,13 @@ class OrderChangeManager:
@app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
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):
sales_channel: str='web', shown_total=None, customer=None, override_now_dt: datetime=None,
api_meta: dict=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)
sales_channel, shown_total, customer, api_meta)
except LockTimeoutException:
self.retry()
except (MaxRetriesExceededError, LockTimeoutException):
+1
View File
@@ -105,6 +105,7 @@ def preview(event: int, provider: str):
order = event.orders.create(status=Order.STATUS_PENDING, datetime=now(),
email='sample@pretix.eu',
locale=event.settings.locale,
sales_channel=event.organizer.sales_channels.get(identifier="web"),
expires=now(), code="PREVIEW1234", total=119)
scheme = PERSON_NAME_SCHEMES[event.settings.name_scheme]
+4
View File
@@ -1478,6 +1478,10 @@ DEFAULTS = {
min_value=1,
required=True,
widget=forms.NumberInput(),
help_text=_('With an increased limit, a customer may request more than one ticket for a specific product '
'using the same, unique email address. However, regardless of this setting, they will need to '
'fill the waitlist form multiple times if they want more than one ticket, as every entry only '
'grants one single ticket at a time.'),
)
},
'show_checkin_number_user': {
+10 -12
View File
@@ -40,7 +40,6 @@ from urllib.parse import urlencode
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import UploadedFile
from django.db.models import Max
from django.forms.formsets import DELETION_FIELD_NAME
from django.urls import reverse
@@ -63,9 +62,9 @@ from pretix.base.models import (
from pretix.base.models.items import ItemAddOn, ItemBundle, ItemMetaValue
from pretix.base.signals import item_copy_data
from pretix.control.forms import (
ButtonGroupRadioSelect, ItemMultipleChoiceField,
SalesChannelCheckboxSelectMultiple, SizeValidationMixin,
SplitDateTimeField, SplitDateTimePickerWidget,
ButtonGroupRadioSelect, ExtFileField, ItemMultipleChoiceField,
SalesChannelCheckboxSelectMultiple, SplitDateTimeField,
SplitDateTimePickerWidget,
)
from pretix.control.forms.widgets import Select2, Select2ItemVarMulti
from pretix.helpers.models import modelcopy
@@ -562,6 +561,13 @@ class TicketNullBooleanSelect(forms.NullBooleanSelect):
class ItemUpdateForm(I18nModelForm):
picture = ExtFileField(
label=_('Product picture'),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
required=False,
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['tax_rule'].queryset = self.instance.event.tax_rules.all()
@@ -751,14 +757,6 @@ class ItemUpdateForm(I18nModelForm):
return d
def clean_picture(self):
value = self.cleaned_data.get('picture')
if isinstance(value, UploadedFile) and value.size > settings.FILE_UPLOAD_MAX_SIZE_IMAGE:
raise forms.ValidationError(_("Please do not upload files larger than {size}!").format(
size=SizeValidationMixin._sizeof_fmt(settings.FILE_UPLOAD_MAX_SIZE_IMAGE)
))
return value
class Meta:
model = Item
localized_fields = '__all__'
+9 -2
View File
@@ -58,7 +58,8 @@ from pretix.api.models import WebHook
from pretix.api.webhooks import get_all_webhook_events
from pretix.base.customersso.oidc import oidc_validate_and_complete_config
from pretix.base.forms import (
I18nMarkdownTextarea, I18nModelForm, PlaceholderValidator, SettingsForm,
SECRET_REDACTED, I18nMarkdownTextarea, I18nModelForm, PlaceholderValidator,
SecretKeySettingsField, SettingsForm,
)
from pretix.base.forms.questions import (
NamePartsFormField, WrappedPhoneNumberPrefixWidget, get_country_by_locale,
@@ -958,7 +959,7 @@ class SSOProviderForm(I18nModelForm):
label=pgettext_lazy('sso_oidc', 'Client ID'),
required=False,
)
config_oidc_client_secret = forms.CharField(
config_oidc_client_secret = SecretKeySettingsField(
label=pgettext_lazy('sso_oidc', 'Client secret'),
required=False,
)
@@ -1015,7 +1016,13 @@ class SSOProviderForm(I18nModelForm):
if self.instance and self.instance.method == method:
f.initial = self.instance.configuration.get(suffix)
def _unmask_secret_fields(self):
for k, v in self.cleaned_data.items():
if isinstance(self.fields.get(k), SecretKeySettingsField) and self.cleaned_data.get(k) == SECRET_REDACTED:
self.cleaned_data[k] = self.fields[k].initial
def clean(self):
self._unmask_secret_fields()
data = self.cleaned_data
if not data.get("method"):
return data
+8
View File
@@ -98,6 +98,14 @@ class ControlFieldRenderer(FieldRenderer):
attrs = ''
return '<div class="{klass}"{attrs}>{html}</div>'.format(klass=self.get_form_group_class(), html=html, attrs=attrs)
def wrap_widget(self, html):
if isinstance(self.widget, CheckboxInput):
css_class = "checkbox"
if self.field.field.disabled:
css_class += " disabled"
html = f'<div class="{css_class}">{html}</div>'
return html
class ControlFieldWithVisibilityRenderer(ControlFieldRenderer):
def __init__(self, *args, **kwargs):
@@ -136,9 +136,15 @@
{% if i.tax_rule and i.default_price %}
<br/>
<small class="text-muted">
{% blocktrans trimmed with rate=i.tax_rule.rate|floatformat:-2 taxname=i.tax_rule.name|default:s_taxes %}
incl. {{ rate }}% {{ taxname }}
{% endblocktrans %}
{% if not i.tax_rule.price_includes_tax %}
{% blocktrans trimmed with rate=i.tax_rule.rate|floatformat:-2 taxname=i.tax_rule.name %}
<strong>plus</strong> {{ rate }}% {{ taxname }}
{% endblocktrans %}
{% else %}
{% blocktrans trimmed with rate=i.tax_rule.rate|floatformat:-2 taxname=i.tax_rule.name|default:s_taxes %}
incl. {{ rate }}% {{ taxname }}
{% endblocktrans %}
{% endif %}
</small>
{% endif %}
</td>
@@ -57,7 +57,7 @@
{% for se in request.event.subevents.all %}
<option value="{{ se.id }}"
{% if request.GET.subevent|add:0 == se.id %}selected="selected"{% endif %}>
{{ se.name }} {{ se.get_date_range_display }}
{{ se }}
</option>
{% endfor %}
</select>
@@ -119,7 +119,7 @@
{% for se in request.event.subevents.all %}
<option value="{{ se.id }}"
{% if request.GET.subevent|add:0 == se.id %}selected="selected"{% endif %}>
{{ se.name }} {{ se.get_date_range_display }}
{{ se }}
</option>
{% endfor %}
</select>
@@ -195,7 +195,7 @@
{% endif %}
</td>
{% if request.event.has_subevents %}
<td>{{ e.subevent.name }} {{ e.subevent.get_date_range_display }}</td>
<td>{{ e.subevent }}</td>
{% endif %}
<td>
{{ e.created|date:"SHORT_DATETIME_FORMAT" }}
+1 -1
View File
@@ -400,7 +400,7 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
if key.startswith("plugin:"):
module = key.split(":")[1]
if value == "enable" and module in plugins_available:
if getattr(plugins_available[module].app, 'restricted', False):
if getattr(plugins_available[module], 'restricted', False):
if module not in request.event.settings.allowed_restricted_plugins:
continue
+1 -1
View File
@@ -103,7 +103,7 @@ class ItemList(ListView):
def get_queryset(self):
return Item.objects.filter(
event=self.request.event
).annotate(
).select_related("tax_rule").annotate(
var_count=Count('variations')
).prefetch_related("category", "limit_sales_channels").order_by(
F('category__position').asc(nulls_first=True),
+1 -1
View File
@@ -3161,7 +3161,7 @@ class ChannelUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMix
}
def form_valid(self, form):
if form.has_changed() or self.formset.has_changed():
if form.has_changed():
self.object.log_action('pretix.saleschannel.changed', user=self.request.user, data={
k: getattr(self.object, k)
for k in form.changed_data
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2024-05-30 17:00+0000\n"
"PO-Revision-Date: 2024-07-10 15:00+0000\n"
"Last-Translator: Nikolai <nikolai@lengefeldt.de>\n"
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
"da/>\n"
@@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.5.5\n"
"X-Generator: Weblate 5.6.1\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -261,7 +261,7 @@ msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
msgid "Ticket not paid"
msgstr ""
msgstr "Billet ikke betalt"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
msgid "This ticket is not yet paid. Do you want to continue anyways?"
@@ -281,11 +281,11 @@ msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56
msgid "Ticket already used"
msgstr ""
msgstr "Billet allerede i brug"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57
msgid "Information required"
msgstr ""
msgstr "Kræver information"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58
msgid "Unknown ticket"
@@ -301,15 +301,15 @@ msgstr "Adgang ikke tilladt"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
msgid "Ticket code revoked/changed"
msgstr ""
msgstr "Billetkode trukket tilbage/ændret"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
msgid "Ticket blocked"
msgstr ""
msgstr "Billet blokeret"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
msgid "Ticket not valid at this time"
msgstr ""
msgstr "Billetten er ikke gyldig på dette tidspunkt"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order canceled"
@@ -317,11 +317,11 @@ msgstr "Bestilling annulleret"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Ticket code is ambiguous on list"
msgstr ""
msgstr "Billetkoden er flertydig på listen"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Order not approved"
msgstr ""
msgstr "Bestilling ikke godkendt"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
#, fuzzy
@@ -472,7 +472,7 @@ msgstr "Produktvariation"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:107
msgid "Gate"
msgstr ""
msgstr "Indgang"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:111
msgid "Current date and time"
@@ -548,7 +548,7 @@ msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:186
msgid "Event admission"
msgstr ""
msgstr "Adgang til arrangementet"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:187
msgid "custom date and time"
+23 -23
View File
@@ -5,7 +5,7 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-30 18:55+0000\n"
"PO-Revision-Date: 2024-06-30 21:07+0000\n"
"PO-Revision-Date: 2024-07-19 22: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 5.6.1\n"
"X-Generator: Weblate 5.6.2\n"
"X-Poedit-Bookmarks: -1,-1,904,-1,-1,-1,-1,-1,-1,-1\n"
#: pretix/_base_settings.py:78
@@ -3536,7 +3536,7 @@ msgstr "Sie können keinen Ticketcode verwenden, der bereits existiert."
#: pretix/base/modelimport_orders.py:490
msgid "Please enter a valid language code."
msgstr "Bitte geben Sie einen gültigen Sprachcode ein."
msgstr "Bitte einen gültigen Sprachcode eingeben."
#: pretix/base/modelimport_orders.py:558 pretix/base/modelimport_orders.py:560
msgid "Please enter a valid sales channel."
@@ -4921,8 +4921,8 @@ msgid ""
"The duration of the membership is the same as the duration of the event or "
"event series date"
msgstr ""
"Die Dauer der Mitgliedschaft entspricht der Dauer der Veranstaltung oder des "
"Termins"
"Die Dauer der Mitgliedschaft entspricht der Dauer der Veranstaltung bzw. in "
"Veranstaltungsreihen des gebuchten Termins"
#: pretix/base/models/items.py:660
msgid "Membership duration in days"
@@ -9608,7 +9608,7 @@ msgstr ""
#: pretix/base/settings.py:1346
msgid "Ask search engines not to index the ticket shop"
msgstr "Der Ticket-Shop soll von Suchmaschinen nicht indiziert werden"
msgstr "Der Ticketshop soll von Suchmaschinen nicht indiziert werden"
#: pretix/base/settings.py:1355
msgid "Show variations of a product expanded by default"
@@ -10874,7 +10874,7 @@ msgstr ""
"Sie haben sich auf die Warteliste für {event} \n"
"für das Produkt {product} eingetragen.\n"
"\n"
"Wir haben nun ein Ticket für Sie! Sie können es in unserem Ticket-Shop "
"Wir haben nun ein Ticket für Sie! Sie können es in unserem Ticketshop "
"erwerben,\n"
"indem Sie in den nächsten {hours} Stunden den folgenden Gutscheincode "
"eingeben:\n"
@@ -11971,7 +11971,7 @@ msgid ""
"If you just configured this as a domain for your ticket shop, you now need "
"to set this up as a \"custom domain\" in your organizer account."
msgstr ""
"Wenn Sie gerade diese Domain für Ihren Ticket-Shop eingerichtet haben, "
"Wenn Sie gerade diese Domain für Ihren Ticketshop eingerichtet haben, "
"müssen Sie diese nun in Ihrem Veranstalterkonto als \"eigene Domain\" "
"eintragen."
@@ -18403,7 +18403,7 @@ msgid ""
"You can instead take your shop offline. This will hide it from everyone "
"except from the organizer teams you configured to have access to the event."
msgstr ""
"Sie können stattdessen den Ticket-Shop abschalten. Dies wird die "
"Sie können stattdessen den Ticketshop abschalten. Dies wird die "
"Veranstaltung vor allen außer den zugewiesenen Veranstalter-Teams verstecken."
#: pretix/control/templates/pretixcontrol/event/delete.html:66
@@ -18629,14 +18629,14 @@ msgid ""
"Your ticket shop is currently not live. It is thus only visible to you and "
"your team, not to any visitors."
msgstr ""
"Ihr Ticket-Shop ist zur Zeit offline und daher nur für Sie und Ihr Team "
"Ihr Ticketshop ist zur Zeit offline und daher nur für Sie und Ihr Team "
"verfügbar und nicht für Besucher."
#: pretix/control/templates/pretixcontrol/event/live.html:41
msgid ""
"To publish your ticket shop, you first need to resolve the following issues:"
msgstr ""
"Um Ihren Ticket-Shop zu veröffentlichen, müssen Sie zuerst die folgenden "
"Um Ihren Ticketshop zu veröffentlichen, müssen Sie zuerst die folgenden "
"Probleme beheben:"
#: pretix/control/templates/pretixcontrol/event/live.html:51
@@ -18646,7 +18646,7 @@ msgstr "Shop veröffentlichen"
#: pretix/control/templates/pretixcontrol/event/live.html:59
msgid "If you want to, you can publish your ticket shop now."
msgstr "Sie können Ihren Ticket-Shop jederzeit veröffentlichen."
msgstr "Sie können Ihren Ticketshop jederzeit veröffentlichen."
#: pretix/control/templates/pretixcontrol/event/live.html:83
msgid ""
@@ -19488,7 +19488,7 @@ msgid ""
"website. This way, your visitors can buy their ticket right away without "
"leaving your website."
msgstr ""
"Das pretix-Widget ist ein Weg, den Ticket-Shop in Ihre Event-Website "
"Das pretix-Widget ist ein Weg, den Ticketshop in Ihre Event-Website "
"einzubetten. Auf diese Weise können Besucher Ihrer Website ein Ticket "
"erwerben, ohne die Website verlassen zu müssen."
@@ -19515,7 +19515,7 @@ msgid ""
"JavaScript is disabled in your browser. To access our ticket shop without "
"JavaScript, please &lt;a %(a_attr)s&gt;click here&lt;/a&gt;."
msgstr ""
"JavaScript ist in Ihrem Browser deaktiviert. Um unseren Ticket-Shop ohne "
"JavaScript ist in Ihrem Browser deaktiviert. Um unseren Ticketshop ohne "
"JavaScript aufzurufen, klicken Sie bitte &lt;a %(a_attr)s&gt;hier&lt;/a&gt;."
#: pretix/control/templates/pretixcontrol/event/widget.html:64
@@ -19580,7 +19580,7 @@ msgid ""
"less than 10 characters that can be easily remembered, but you can also "
"choose to use a random value."
msgstr ""
"Dies ist die Adresse, unter der Ihr Ticket-Shop verfügbar sein wird. Sie "
"Dies ist die Adresse, unter der Ihr Ticketshop verfügbar sein wird. Sie "
"sollte kurz sein und darf nur Kleinbuchstaben, Zahlen, Punkte und "
"Bindestriche enthalten. Sie muss unter Ihren Veranstaltungen einmalig sein. "
"Wir empfehlen eine Abkürzung oder ein Datum mit weniger als 10 Zeichen, das "
@@ -22464,7 +22464,7 @@ msgstr "Veröffentlichen Sie Ihren Shop"
#: pretix/control/templates/pretixcontrol/orders/index.html:27
msgid "Go to the ticket shop"
msgstr "Zum Ticket-Shop"
msgstr "Zum Ticketshop"
#: pretix/control/templates/pretixcontrol/orders/index.html:35
msgid "Search query:"
@@ -23102,7 +23102,7 @@ msgid ""
"usage, the legal details in your specific jurisdiction, or the agreements "
"you have with third parties such as payment or tracking providers."
msgstr ""
"Trotzdem bleibt es Ihre Verantwortung, dass Ihr Ticket-Shop allen "
"Trotzdem bleibt es Ihre Verantwortung, dass Ihr Ticketshop allen "
"anwendbaren Gesetzen entspricht. Wir versuchen mit diesen Einstellungen zu "
"helfen, aber können keine Haftung übernehmen, da wir unter anderem die "
"genaue Konfiguration Ihres Ticketshops, die rechtlichen Details der "
@@ -25624,7 +25624,7 @@ msgstr "{quota} übrig"
#: pretix/control/views/dashboards.py:269
msgid "Your ticket shop is"
msgstr "Ihr Ticket-Shop ist"
msgstr "Ihr Ticketshop ist"
#: pretix/control/views/dashboards.py:269
msgid "Click here to change"
@@ -31490,7 +31490,7 @@ msgstr "Warnung"
#: pretix/presale/templates/pretixpresale/event/base.html:127
#: pretix/presale/templates/pretixpresale/event/base.html:201
msgid "This ticket shop is currently in test mode."
msgstr "Dieser Ticket-Shop ist momentan im Testmodus."
msgstr "Dieser Ticketshop ist momentan im Testmodus."
#: pretix/presale/templates/pretixpresale/event/base.html:130
#: pretix/presale/templates/pretixpresale/event/base.html:204
@@ -32857,7 +32857,7 @@ msgstr "Shop ausgeschaltet"
#: pretix/presale/templates/pretixpresale/event/offline.html:9
msgid "This ticket shop is currently turned off."
msgstr "Dieser Ticket-Shop ist im Moment nicht aktiv."
msgstr "Dieser Ticketshop ist im Moment nicht aktiv."
#: pretix/presale/templates/pretixpresale/event/offline.html:10
msgid "It is only accessible to authenticated team members."
@@ -33684,7 +33684,7 @@ msgid ""
"open source ticket sales software</a>."
msgstr ""
"Dies ist eine selbst-gehostete Installation von <a %(a_attr)s>pretix, dem "
"freundlichen Open Source Ticket-Shop</a>."
"freundlichen Open Source Ticketshop</a>."
#: pretix/presale/templates/pretixpresale/index.html:15
msgid ""
@@ -34215,7 +34215,7 @@ msgstr ""
#: pretix/presale/views/widget.py:372
msgid "This ticket shop is currently disabled."
msgstr "Dieser Ticket-Shop ist im Moment nicht verfügbar."
msgstr "Dieser Ticketshop ist im Moment nicht verfügbar."
#: pretix/presale/views/widget.py:386
msgid "The selected date does not exist in this event series."
@@ -34336,7 +34336,7 @@ msgstr "Kosovo"
#~ msgstr "Sie können nicht fortfahren."
#~ msgid "The selected ticket shop is currently not available."
#~ msgstr "Der ausgewählte Ticket-Shop ist im Moment nicht verfügbar."
#~ msgstr "Der ausgewählte Ticketshop ist im Moment nicht verfügbar."
#~ msgid "Needs to be enabled in your Stripe account first."
#~ msgstr "Muss erst im Stripe-Account aktiviert werden."
+3 -3
View File
@@ -871,12 +871,12 @@ msgstr "minimale Bestellmenge: %s"
#: pretix/static/pretixpresale/js/widget/widget.js:39
msgctxt "widget"
msgid "Close ticket shop"
msgstr "Ticket-Shop schließen"
msgstr "Ticketshop schließen"
#: pretix/static/pretixpresale/js/widget/widget.js:40
msgctxt "widget"
msgid "The ticket shop could not be loaded."
msgstr "Der Ticket-Shop konnte nicht geladen werden."
msgstr "Der Ticketshop konnte nicht geladen werden."
#: pretix/static/pretixpresale/js/widget/widget.js:41
msgctxt "widget"
@@ -890,7 +890,7 @@ msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:43
msgctxt "widget"
msgid "Open ticket shop"
msgstr "Ticket-Shop öffnen"
msgstr "Ticketshop öffnen"
#: pretix/static/pretixpresale/js/widget/widget.js:44
msgctxt "widget"
@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-30 18:55+0000\n"
"PO-Revision-Date: 2024-06-30 21:07+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"PO-Revision-Date: 2024-07-20 13:00+0000\n"
"Last-Translator: JnnisCanis <me@jnnis.de>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
"pretix/pretix/de_Informal/>\n"
"Language: 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 5.6.1\n"
"X-Generator: Weblate 5.6.2\n"
#: pretix/_base_settings.py:78
msgid "English"
@@ -3536,7 +3536,7 @@ msgstr "Sie können keinen Ticketcode verwenden, der bereits existiert."
#: pretix/base/modelimport_orders.py:490
msgid "Please enter a valid language code."
msgstr "Bitte gib einen gültigen Sprachcode ein."
msgstr "Bitte einen gültigen Sprachcode eingeben."
#: pretix/base/modelimport_orders.py:558 pretix/base/modelimport_orders.py:560
msgid "Please enter a valid sales channel."
@@ -4917,8 +4917,8 @@ msgid ""
"The duration of the membership is the same as the duration of the event or "
"event series date"
msgstr ""
"Die Dauer der Mitgliedschaft entspricht der Dauer der Veranstaltung oder des "
"Termins"
"Die Dauer der Mitgliedschaft entspricht der Dauer der Veranstaltung bzw. in "
"Veranstaltungsreihen des gebuchten Termins"
#: pretix/base/models/items.py:660
msgid "Membership duration in days"
@@ -6671,11 +6671,10 @@ msgid ""
"rel=\"noopener\">Click here for detailed information on what this does.</a> "
"Don't forget to set the correct fees above!"
msgstr ""
"Wir empfehlen, diese Option zu aktivieren, wenn du die Gebühren Ihres "
"Zahlungsdienstleisters an Ihre Kunden weitergeben willst. Denk daran, oben "
"die richtigen Gebührensätze des Zahlungsanbieters einzustellen. <a "
"href=\"{docs_url}\" target=\"_blank\" rel=\"noopener\">Weitere "
"Informationen</a>."
"Wir empfehlen, diese Option zu aktivieren, wenn Du die Gebühren deines "
"Zahlungsdienstleisters an deine Kunden weitergeben willst. Denke daran, oben "
"die richtigen Gebührensätze des Zahlungsanbieters einzustellen. <a href=\""
"{docs_url}\" target=\"_blank\" rel=\"noopener\">Weitere Informationen</a>."
#: pretix/base/payment.py:394
msgid "Text on invoices"
@@ -9239,9 +9238,9 @@ msgid ""
msgstr ""
"Anzahl an Minuten, die ein Benutzer nach Abschicken der Bestellung hat, um "
"die Zahlung abzuschließen. Benutze dies nur, wenn du ausschließlich "
"Zahlungsmethoden mit sofortiger Bestätigung anbietest. Bitte beachte das das "
"tatsächliche Zeitfenster aus technischen Gründen einige Minuten länger sein "
"kann."
"Zahlungsmethoden mit sofortiger Bestätigung anbietest. Bitte beachte, dass "
"das tatsächliche Zeitfenster aus technischen Gründen einige Minuten länger "
"sein kann."
#: pretix/base/settings.py:920
msgid "Last date of payments"
@@ -9594,7 +9593,7 @@ msgstr ""
#: pretix/base/settings.py:1346
msgid "Ask search engines not to index the ticket shop"
msgstr "Der Ticket-Shop soll von Suchmaschinen nicht indiziert werden"
msgstr "Der Ticketshop soll von Suchmaschinen nicht indiziert werden"
#: pretix/base/settings.py:1355
msgid "Show variations of a product expanded by default"
@@ -9914,7 +9913,8 @@ msgstr "Keine Änderungen an bestehenden Bestellungen"
#: pretix/base/settings.py:1665 pretix/base/settings.py:1674
msgid "Only the person who ordered can make changes"
msgstr ""
"Nur die Person, die die Bestellung aufgegeben hat, kann Änderungen vornehmen"
"Nur die Person, welche die Bestellung aufgegeben hat, kann Änderungen "
"vornehmen"
#: pretix/base/settings.py:1666 pretix/base/settings.py:1675
msgid "Both the attendee and the person who ordered can make changes"
@@ -10854,9 +10854,9 @@ msgstr ""
"Hallo,\n"
"\n"
"du hast dich auf die Warteliste für {event} \n"
"für das Produkt {product} eingetragen.\n"
"und das Produkt {product} eingetragen.\n"
"\n"
"Wir haben nun ein Ticket für dich! Du kannst es in unserem Ticket-Shop "
"Wir haben nun ein Ticket für dich! Du kannst es in unserem Ticketshop "
"erwerben,\n"
"indem du in den nächsten {hours} Stunden den folgenden Gutscheincode "
"eingibst:\n"
@@ -10872,9 +10872,8 @@ msgstr ""
"weitergeben,\n"
"wenn du den Gutschein nicht in diesem Zeitraum einlöst.\n"
"\n"
"Wenn du das Ticket NICHT mehr möchtest, bitten wir dich folgenden Link zu "
"klicken\n"
"um uns Bescheid zu sagen. So können wir das Ticket schnellstmöglich an die "
"Wenn du das Ticket NICHT mehr möchtest, sag uns bitte durch Klicken auf "
"folgenden Link Bescheid. So können wir das Ticket schnellstmöglich an die "
"nächste\n"
"wartende Person auf der Warteliste weitergeben:\n"
"\n"
@@ -11950,7 +11949,7 @@ msgid ""
"If you just configured this as a domain for your ticket shop, you now need "
"to set this up as a \"custom domain\" in your organizer account."
msgstr ""
"Wenn du gerade diese Domain für deinen Ticket-Shop eingerichtet hast, musst "
"Wenn du gerade diese Domain für deinen Ticketshop eingerichtet hast, musst "
"du diese nun in deinem Veranstalterkonto als \"eigene Domain\" eintragen."
#: pretix/base/templates/403.html:4 pretix/base/templates/403.html:8
@@ -13976,7 +13975,7 @@ msgid ""
"set the name here."
msgstr ""
"Wenn du den \"powered by\"-Text in der Fußzeile anpassen willst, um den "
"Namen Ihrer Firma oder Organisation aufzunehmen (wenn du Änderungen an "
"Namen deiner Firma oder Organisation aufzunehmen (wenn du Änderungen an "
"pretix vorgenommen hast), kannst du hier den Namen eintragen."
#: pretix/control/forms/global_settings.py:205
@@ -14641,7 +14640,7 @@ msgstr ""
"Erstattungen angezeigt. Wenn diese Option mit der Option für automatische "
"Erstattungen kombiniert wird, werden ausschließlich Zahlungen mit einer "
"Zahlungsart, die keine automatischen Erstattungen unterstützt, automatisch "
"als manuelle Erstattungen auf Ihrer Erstattungsliste als \"zu erledigen\" "
"als manuelle Erstattungen auf deiner Erstattungsliste als \"zu erledigen\" "
"auftauchen. Wähle diese Option nicht, wenn du Bestellungen mit anderen "
"Bestellungen verrechnen oder als Gutschein erstatten möchtest."
@@ -17466,7 +17465,7 @@ msgstr "Filter"
#: pretix/control/templates/pretixcontrol/checkin/checkins.html:50
msgid "Your search did not match any check-ins."
msgstr "Ihre Filter treffen auf keine Check-ins zu."
msgstr "Deine Filter treffen auf keine Check-ins zu."
#: pretix/control/templates/pretixcontrol/checkin/checkins.html:52
msgid "You haven't scanned any tickets yet."
@@ -18371,7 +18370,7 @@ msgid ""
"You can instead take your shop offline. This will hide it from everyone "
"except from the organizer teams you configured to have access to the event."
msgstr ""
"Du kannst stattdessen den Ticket-Shop abschalten. Dies wird die "
"Du kannst stattdessen den Ticketshop abschalten. Dies wird die "
"Veranstaltung vor allen außer den zugewiesenen Veranstalter-Teams verstecken."
#: pretix/control/templates/pretixcontrol/event/delete.html:66
@@ -18596,14 +18595,14 @@ msgid ""
"Your ticket shop is currently not live. It is thus only visible to you and "
"your team, not to any visitors."
msgstr ""
"Dein Ticket-Shop ist zur Zeit offline und daher nur für dich und dein Team "
"Dein Ticketshop ist zur Zeit offline und daher nur für dich und dein Team "
"verfügbar und nicht für Besucher."
#: pretix/control/templates/pretixcontrol/event/live.html:41
msgid ""
"To publish your ticket shop, you first need to resolve the following issues:"
msgstr ""
"Um deinen Ticket-Shop zu veröffentlichen, musst du zuerst die folgenden "
"Um deinen Ticketshop zu veröffentlichen, musst du zuerst die folgenden "
"Probleme beheben:"
#: pretix/control/templates/pretixcontrol/event/live.html:51
@@ -18613,7 +18612,7 @@ msgstr "Shop veröffentlichen"
#: pretix/control/templates/pretixcontrol/event/live.html:59
msgid "If you want to, you can publish your ticket shop now."
msgstr "Du kannst deinen Ticket-Shop jederzeit veröffentlichen."
msgstr "Du kannst deinen Ticketshop jederzeit veröffentlichen."
#: pretix/control/templates/pretixcontrol/event/live.html:83
msgid ""
@@ -19454,7 +19453,7 @@ msgid ""
"website. This way, your visitors can buy their ticket right away without "
"leaving your website."
msgstr ""
"Das pretix-Widget ist ein Weg, den Ticket-Shop in deine Event-Website "
"Das pretix-Widget ist ein Weg, den Ticketshop in deine Event-Website "
"einzubetten. Auf diese Weise können Besucher deiner Website ein Ticket "
"erwerben, ohne die Website verlassen zu müssen."
@@ -19481,7 +19480,7 @@ msgid ""
"JavaScript is disabled in your browser. To access our ticket shop without "
"JavaScript, please &lt;a %(a_attr)s&gt;click here&lt;/a&gt;."
msgstr ""
"JavaScript ist in deinem Browser deaktiviert. Um unseren Ticket-Shop ohne "
"JavaScript ist in deinem Browser deaktiviert. Um unseren Ticketshop ohne "
"JavaScript aufzurufen, klicke bitte &lt;a %(a_attr)s&gt;hier&lt;/a&gt;."
#: pretix/control/templates/pretixcontrol/event/widget.html:64
@@ -19546,7 +19545,7 @@ msgid ""
"less than 10 characters that can be easily remembered, but you can also "
"choose to use a random value."
msgstr ""
"Dies ist die Adresse, unter der dein Ticket-Shop verfügbar sein wird. Sie "
"Dies ist die Adresse, unter der dein Ticketshop verfügbar sein wird. Sie "
"sollte kurz sein und darf nur Kleinbuchstaben, Zahlen, Punkte und "
"Bindestriche enthalten. Sie muss unter deinen Veranstaltungen einmalig sein. "
"Wir empfehlen eine Abkürzung oder ein Datum mit unter 10 Zeichen, das man "
@@ -19807,7 +19806,7 @@ msgid ""
"usage of pretix is in compliance with the Enterprise license you purchased."
msgstr ""
"Der Bericht dient zwei Zwecken: Dem Sammeln nützlicher Informationen um "
"Probleme mit Ihrer pretix-Installation zu analysieren und um zu überprüfen, "
"Probleme mit deiner pretix-Installation zu analysieren und um zu überprüfen, "
"dass deine Nutzung von pretix von der erworbenen pretix-Enterprise-Lizenz "
"abgedeckt ist."
@@ -22425,7 +22424,7 @@ msgstr "Veröffentliche deinen Shop"
#: pretix/control/templates/pretixcontrol/orders/index.html:27
msgid "Go to the ticket shop"
msgstr "Zum Ticket-Shop"
msgstr "Zum Ticketshop"
#: pretix/control/templates/pretixcontrol/orders/index.html:35
msgid "Search query:"
@@ -23063,7 +23062,7 @@ msgid ""
"usage, the legal details in your specific jurisdiction, or the agreements "
"you have with third parties such as payment or tracking providers."
msgstr ""
"Trotzdem bleibt es deine Verantwortung, dass dein Ticket-Shop allen "
"Trotzdem bleibt es deine Verantwortung, dass dein Ticketshop allen "
"anwendbaren Gesetzen entspricht. Wir versuchen mit diesen Einstellungen zu "
"helfen, aber können keine Haftung übernehmen, da wir unter anderem die "
"genaue Konfiguration deines Ticketshops, die rechtlichen Details der "
@@ -25579,7 +25578,7 @@ msgstr "{quota} übrig"
#: pretix/control/views/dashboards.py:269
msgid "Your ticket shop is"
msgstr "Dein Ticket-Shop ist"
msgstr "Dein Ticketshop ist"
#: pretix/control/views/dashboards.py:269
msgid "Click here to change"
@@ -30131,7 +30130,7 @@ msgid ""
"generate you API keys with the recommended permission level for optimal "
"usage with pretix."
msgstr ""
"Der Button oben wird unsere Stripe-App in Ihrem Stripe-Konto installieren "
"Der obige Button wird unsere Stripe-App in deinem Stripe-Konto installieren "
"und API-Schlüssel mit dem empfohlenen Berechtigungslevel für die Verwendung "
"mit pretix generieren."
@@ -30382,7 +30381,7 @@ msgstr ""
#: pretix/plugins/stripe/payment.py:992
msgid "Your payment failed. Please try again."
msgstr "Ihre Zahlung ist fehlgeschlagen, bitte erneut versuchen."
msgstr "Deine Zahlung ist fehlgeschlagen. Bitte versuche es erneut."
#: pretix/plugins/stripe/payment.py:998
#, python-format
@@ -31432,7 +31431,7 @@ msgstr "Warnung"
#: pretix/presale/templates/pretixpresale/event/base.html:127
#: pretix/presale/templates/pretixpresale/event/base.html:201
msgid "This ticket shop is currently in test mode."
msgstr "Dieser Ticket-Shop ist momentan im Testmodus."
msgstr "Dieser Ticketshop ist momentan im Testmodus."
#: pretix/presale/templates/pretixpresale/event/base.html:130
#: pretix/presale/templates/pretixpresale/event/base.html:204
@@ -32466,7 +32465,7 @@ msgid ""
"If you click the link in our email, you will be able to download your "
"tickets here."
msgstr ""
"Wenn du den Link in Ihrer E-Mail anklickst, kannst du deine Tickets hier "
"Wenn du den Link in deiner E-Mail anklickst, kannst du deine Tickets hier "
"herunterladen."
#: pretix/presale/templates/pretixpresale/event/fragment_downloads.html:30
@@ -32794,7 +32793,7 @@ msgstr "Shop ausgeschaltet"
#: pretix/presale/templates/pretixpresale/event/offline.html:9
msgid "This ticket shop is currently turned off."
msgstr "Dieser Ticket-Shop ist im Moment nicht aktiv."
msgstr "Dieser Ticketshop ist im Moment nicht aktiv."
#: pretix/presale/templates/pretixpresale/event/offline.html:10
msgid "It is only accessible to authenticated team members."
@@ -33622,7 +33621,7 @@ msgid ""
"open source ticket sales software</a>."
msgstr ""
"Dies ist eine selbst-gehostete Installation von <a %(a_attr)s>pretix, dem "
"freundlichen Open Source Ticket-Shop</a>."
"freundlichen Open Source Ticketshop</a>."
#: pretix/presale/templates/pretixpresale/index.html:15
msgid ""
@@ -33691,7 +33690,7 @@ msgstr "Konto erstellen"
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:7
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:10
msgid "Your membership"
msgstr "Ihre Mitgliedschaft"
msgstr "Deine Mitgliedschaft"
#: pretix/presale/templates/pretixpresale/organizers/customer_password.html:6
#: pretix/presale/templates/pretixpresale/organizers/customer_resetpw.html:6
@@ -34150,7 +34149,7 @@ msgstr ""
#: pretix/presale/views/widget.py:372
msgid "This ticket shop is currently disabled."
msgstr "Dieser Ticket-Shop ist im Moment nicht verfügbar."
msgstr "Dieser Ticketshop ist im Moment nicht verfügbar."
#: pretix/presale/views/widget.py:386
msgid "The selected date does not exist in this event series."
@@ -34271,7 +34270,7 @@ msgstr "Kosovo"
#~ msgstr "Du kannst daher nicht fortfahren."
#~ msgid "The selected ticket shop is currently not available."
#~ msgstr "Der ausgewählte Ticket-Shop ist im Moment nicht verfügbar."
#~ msgstr "Der ausgewählte Ticketshop ist im Moment nicht verfügbar."
#~ msgid "Needs to be enabled in your Stripe account first."
#~ msgstr "Muss erst im Stripe-Account aktiviert werden."
@@ -870,12 +870,12 @@ msgstr "minimale Bestellmenge: %s"
#: pretix/static/pretixpresale/js/widget/widget.js:39
msgctxt "widget"
msgid "Close ticket shop"
msgstr "Ticket-Shop schließen"
msgstr "Ticketshop schließen"
#: pretix/static/pretixpresale/js/widget/widget.js:40
msgctxt "widget"
msgid "The ticket shop could not be loaded."
msgstr "Der Ticket-Shop konnte nicht geladen werden."
msgstr "Der Ticketshop konnte nicht geladen werden."
#: pretix/static/pretixpresale/js/widget/widget.js:41
msgctxt "widget"
@@ -889,7 +889,7 @@ msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:43
msgctxt "widget"
msgid "Open ticket shop"
msgstr "Ticket-Shop öffnen"
msgstr "Ticketshop öffnen"
#: pretix/static/pretixpresale/js/widget/widget.js:44
msgctxt "widget"
File diff suppressed because it is too large Load Diff
+11 -11
View File
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2024-06-25 01:00+0000\n"
"PO-Revision-Date: 2024-07-19 08:56+0000\n"
"Last-Translator: Reece Needham <nouveaureece@protonmail.com>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/"
"pretix-js/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 5.5.5\n"
"X-Generator: Weblate 5.6.2\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -254,7 +254,7 @@ msgstr "Cancelar"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
msgid "Ticket not paid"
msgstr "Ticket por pagar"
msgstr "Entrada no pagada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
msgid "This ticket is not yet paid. Do you want to continue anyways?"
@@ -274,7 +274,7 @@ msgstr "Salida registrada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56
msgid "Ticket already used"
msgstr "Este ticket ya fue utilizado"
msgstr "Esta entrada ya fue utilizada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57
msgid "Information required"
@@ -282,11 +282,11 @@ msgstr "Información requerida"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58
msgid "Unknown ticket"
msgstr "Ticket no encontrado"
msgstr "Entrada desconocida"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:59
msgid "Ticket type not allowed here"
msgstr "Tipo de ticket no está permitido"
msgstr "Tipo de entrada no está permitido"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
msgid "Entry not allowed"
@@ -294,19 +294,19 @@ msgstr "Entrada no permitida"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
msgid "Ticket code revoked/changed"
msgstr "Código de ticket revocado/cambiado"
msgstr "Código de entrada revocada/cambiada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
msgid "Ticket blocked"
msgstr "Ticket bloqueado"
msgstr "Entrada bloqueada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
msgid "Ticket not valid at this time"
msgstr "Ticket no válido en este momento"
msgstr "Entrada no válida en este momento"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order canceled"
msgstr "Orden cancelada"
msgstr "Pedido cancelado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Ticket code is ambiguous on list"
@@ -591,7 +591,7 @@ msgstr "Objeto"
#: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design"
msgstr "Diseño del ticket"
msgstr "Diseño del entrada"
#: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed."
File diff suppressed because it is too large Load Diff
+7 -11
View File
@@ -7,16 +7,16 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-30 18:55+0000\n"
"PO-Revision-Date: 2024-04-19 23:00+0000\n"
"Last-Translator: Christiaan de Die le Clercq <contact@techwolf12.nl>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/"
">\n"
"PO-Revision-Date: 2024-07-11 02:00+0000\n"
"Last-Translator: Dirk-jan mollema <dirkjan@outsidersecurity.nl>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/>"
"\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.4.3\n"
"X-Generator: Weblate 5.6.1\n"
#: pretix/_base_settings.py:78
msgid "English"
@@ -25927,10 +25927,8 @@ msgstr "Voucherhistorie"
#: pretix/control/templates/pretixcontrol/vouchers/import_start.html:6
#: pretix/control/templates/pretixcontrol/vouchers/index.html:81
#: pretix/control/templates/pretixcontrol/vouchers/index.html:94
#, fuzzy
#| msgid "Import mode"
msgid "Import vouchers"
msgstr "Importmodus"
msgstr "Importeer vouchers"
#: pretix/control/templates/pretixcontrol/vouchers/index.html:10
msgid ""
@@ -33194,10 +33192,8 @@ msgid "There are no add-ons available for this product."
msgstr "Er zijn geen add-ons beschikbaar voor dit product."
#: pretix/presale/templates/pretixpresale/event/fragment_availability.html:6
#, fuzzy
#| msgid "Enter a voucher code below to buy this ticket."
msgid "Enter a voucher code below to buy this product."
msgstr "Voer hieronder een vouchercode in om dit ticket te kopen."
msgstr "Voer hieronder een vouchercode in om dit product te kopen."
#: pretix/presale/templates/pretixpresale/event/fragment_availability.html:10
#, fuzzy
+53 -116
View File
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-30 18:55+0000\n"
"PO-Revision-Date: 2024-06-27 17:00+0000\n"
"PO-Revision-Date: 2024-07-01 16:00+0000\n"
"Last-Translator: Anarion Dunedain <anarion80@gmail.com>\n"
"Language-Team: Polish <https://translate.pretix.eu/projects/pretix/pretix/pl/"
">\n"
@@ -122,7 +122,7 @@ msgstr "Słowacki"
#: pretix/_base_settings.py:103
msgid "Swedish"
msgstr ""
msgstr "Szwedzki"
#: pretix/_base_settings.py:104
msgid "Spanish"
@@ -640,13 +640,15 @@ msgstr "Sklep online"
#: pretix/base/channels.py:174
msgid "API"
msgstr ""
msgstr "API"
#: pretix/base/channels.py:175
msgid ""
"API sales channels come with no built-in functionality, but may be used for "
"custom integrations."
msgstr ""
"Kanały sprzedaży API nie mają wbudowanych funkcji, ale mogą być używane do "
"niestandardowych integracji."
#: pretix/base/context.py:45
#, python-brace-format
@@ -4041,10 +4043,8 @@ msgid "Position"
msgstr "Pozycja"
#: pretix/base/models/discount.py:68
#, fuzzy
#| msgid "Sales channels"
msgid "All supported sales channels"
msgstr "Kanały sprzedaży"
msgstr "Wszystkie obsługiwane kanały sprzedaży"
#: pretix/base/models/discount.py:89
msgid "Event series handling"
@@ -4257,10 +4257,8 @@ msgid "Seating plan"
msgstr "Plan miejsc"
#: pretix/base/models/event.py:639 pretix/base/models/items.py:618
#, fuzzy
#| msgid "Sales channels"
msgid "Sell on all sales channels"
msgstr "Kanały sprzedaży"
msgstr "Sprzedawaj we wszystkich kanałach sprzedaży"
#: pretix/base/models/event.py:644 pretix/base/models/items.py:623
#: pretix/base/models/items.py:1170 pretix/base/payment.py:417
@@ -4570,9 +4568,6 @@ msgid "{category} (Add-On products)"
msgstr "{category} (Produkty dodatkowe)"
#: pretix/base/models/items.py:128
#, fuzzy
#| msgctxt "checkoutflow"
#| msgid "Add-on products"
msgid "Add-On products"
msgstr "Produkty dodatkowe"
@@ -4859,11 +4854,8 @@ msgstr ""
"przeceny. Ustawienie kosmetyczne bez wzgędlu na faktyczną cenę sprzedaży."
#: pretix/base/models/items.py:624
#, fuzzy
#| msgid "Only sell tickets for this event on the following sales channels."
msgid "Only sell tickets for this product on the selected sales channels."
msgstr ""
"Sprzedaj bilety tylko na to wydarzenie na następujących kanałach sprzedaży."
msgstr "Sprzedaj bilety na ten produkt tylko w wybranych kanałach sprzedaży."
#: pretix/base/models/items.py:629
msgid ""
@@ -5114,6 +5106,8 @@ msgstr "Ten wariant nie będzie sprzedawany po podanej dacie."
#: pretix/base/models/items.py:1165
msgid "Sell on all sales channels the product is sold on"
msgstr ""
"Sprzedaj we wszystkich kanałach sprzedaży, w których sprzedawany jest ten "
"produkt."
#: pretix/base/models/items.py:1171
msgid ""
@@ -6053,9 +6047,6 @@ msgstr "Zaproszenie do zespołu '{team}' dla '{email}'"
#: pretix/base/models/organizer.py:538
#: pretix/control/templates/pretixcontrol/organizers/channels.html:23
#, fuzzy
#| msgctxt "reusable_medium"
#| msgid "Identifier"
msgid "Identifier"
msgstr "Identyfikator"
@@ -12050,6 +12041,8 @@ msgid ""
"This sales channel cannot be used properly since the respective plugin is "
"not active for this event."
msgstr ""
"Ten kanał sprzedaży nie może być używany prawidłowo, ponieważ odpowiednia "
"wtyczka nie jest aktywna dla tego wydarzenia."
#: pretix/base/templates/pretixbase/forms/widgets/portrait_image.html:10
msgid "Upload photo"
@@ -14061,6 +14054,8 @@ msgid ""
"You have selected dynamic validity but have not entered a time period. This "
"would render the tickets unusable."
msgstr ""
"Wybrałeś dynamiczną ważność, ale nie wprowadziłeś okresu czasu. Spowoduje "
"to, że bilety będą bezużyteczne."
#: pretix/control/forms/item.py:858
#, python-format
@@ -14807,10 +14802,8 @@ msgid "The selected organizer has already been invited."
msgstr "Wybrany organizator został już zaproszony."
#: pretix/control/forms/organizer.py:1128
#, fuzzy
#| msgid "A voucher with this code already exists."
msgid "A sales channel with the same identifier already exists."
msgstr "Voucher o tym kodzie już istnieje."
msgstr "Kanał sprzedaży o tym samym identyfikatorze już istnieje."
#: pretix/control/forms/renderers.py:56
#: pretix/control/templates/pretixcontrol/items/question_edit.html:139
@@ -15423,23 +15416,16 @@ msgid "The membership type has been deleted."
msgstr "Typ członkostwa został usunięty."
#: pretix/control/logdisplay.py:360 pretix/control/views/organizer.py:3119
#, fuzzy
#| msgctxt "subevent"
#| msgid "The new date has been created."
msgid "The sales channel has been created."
msgstr "Nowa data została utworzona."
msgstr "Kanał sprzedaży został utworzony."
#: pretix/control/logdisplay.py:361
#, fuzzy
#| msgid "The device has been changed."
msgid "The sales channel has been changed."
msgstr "Urządzenie zostało zmienione."
msgstr "Kanał sprzedaży został zmieniony."
#: pretix/control/logdisplay.py:362
#, fuzzy
#| msgid "The selected list has been deleted."
msgid "The sales channel has been deleted."
msgstr "Wybrana lista została usunięta."
msgstr "Kanał sprzedaży został usunięty."
#: pretix/control/logdisplay.py:363
msgid "The account has been created."
@@ -20190,14 +20176,14 @@ msgstr "Utwórz nową kategorię"
#: pretix/control/templates/pretixcontrol/items/index.html:146
#: pretix/control/templates/pretixcontrol/organizers/properties.html:54
msgid "Move up"
msgstr ""
msgstr "Przesuń w górę"
#: pretix/control/templates/pretixcontrol/items/categories.html:45
#: pretix/control/templates/pretixcontrol/items/discounts.html:142
#: pretix/control/templates/pretixcontrol/items/index.html:147
#: pretix/control/templates/pretixcontrol/organizers/properties.html:55
msgid "Move down"
msgstr ""
msgstr "Przesuń w dół"
#: pretix/control/templates/pretixcontrol/items/categories.html:46
#: pretix/control/templates/pretixcontrol/items/discounts.html:145
@@ -20207,6 +20193,8 @@ msgid ""
"Click and drag this button to reorder. Double click to show buttons for "
"reordering."
msgstr ""
"Kliknij i przeciągnij ten przycisk, aby zmienić kolejność. Kliknij "
"dwukrotnie, aby wyświetlić przyciski do zmiany kolejności."
#: pretix/control/templates/pretixcontrol/items/category.html:27
msgid "Category history"
@@ -20350,15 +20338,12 @@ msgstr ""
"produktu"
#: pretix/control/templates/pretixcontrol/items/discounts.html:111
#, fuzzy
#| msgctxt "discount"
#| msgid "Condition"
msgid "Condition:"
msgstr "Stan"
msgstr "Stan:"
#: pretix/control/templates/pretixcontrol/items/discounts.html:126
msgid "Applies to:"
msgstr ""
msgstr "Dotyczy:"
#: pretix/control/templates/pretixcontrol/items/fragment_quota_availability.html:3
msgid "Closed"
@@ -20398,12 +20383,6 @@ msgid "taxes"
msgstr "podatki"
#: pretix/control/templates/pretixcontrol/items/index.html:10
#, fuzzy
#| msgid ""
#| "Below, you find a list of all available products. You can click on a "
#| "product name to inspect and change product details. You can also use the "
#| "buttons on the right to change the order of products within a give "
#| "category."
msgid ""
"Below, you find a list of all available products. You can click on a product "
"name to inspect and change product details. You can also use the buttons on "
@@ -20412,8 +20391,8 @@ msgid ""
msgstr ""
"Poniżej znajduje się lista wszystkich dostępnych produktów. Możesz kliknąć "
"nazwę produktu, aby sprawdzić i zmienić jego szczegóły. Możesz także użyć "
"przycisków po prawej stronie, aby zmienić kolejność produktów w danej "
"kategorii."
"przycisków po prawej stronie, aby zmienić kolejność produktów lub przenieść "
"produkty do innej kategorii."
#: pretix/control/templates/pretixcontrol/items/index.html:19
msgid "You haven't created any products yet."
@@ -22479,48 +22458,34 @@ msgstr "Wyszukiwanie zamówień"
#: pretix/control/templates/pretixcontrol/organizers/channel_add.html:6
#: pretix/control/templates/pretixcontrol/organizers/channel_add_choice.html:6
#, fuzzy
#| msgid "Sales channel"
msgid "Add sales channel"
msgstr "Kanał sprzedaży"
msgstr "Dodaj kanał sprzedaży"
#: pretix/control/templates/pretixcontrol/organizers/channel_add.html:13
#: pretix/control/templates/pretixcontrol/organizers/channel_edit.html:13
#: pretix/control/templates/pretixcontrol/organizers/channels.html:24
#, fuzzy
#| msgid "Scan type"
msgid "Channel type"
msgstr "Typ skanowania"
msgstr "Typ kanału"
#: pretix/control/templates/pretixcontrol/organizers/channel_delete.html:5
#, fuzzy
#| msgid "Sales channel"
msgid "Delete sales channel:"
msgstr "Kanał sprzedaży"
msgstr "Usuń kanał sprzedaży:"
#: pretix/control/templates/pretixcontrol/organizers/channel_delete.html:10
#, fuzzy
#| msgid "Are you sure you want to delete the gate?"
msgid "Are you sure you want to delete this sales channel?"
msgstr "Czy na pewno chcesz usunąć bramkę?"
msgstr "Czy na pewno chcesz usunąć ten kanał sprzedaży?"
#: pretix/control/templates/pretixcontrol/organizers/channel_delete.html:15
#, fuzzy
#| msgid ""
#| "This membership cannot be deleted since it has been used in an order. "
#| "Change its end date to the past instead."
msgid ""
"This sales channel cannot be deleted since it has already been used to sell "
"orders or because it is a core element of the system."
msgstr ""
"Tego członkostwa nie można usunąć, ponieważ zostało użyte w zamówieniu. "
"Zamiast tego zmień jego datę końcową na przeszłą."
"Tego kanału sprzedaży nie można usunąć, ponieważ został już użyty do "
"sprzedaży zamówień lub jest podstawowym elementem systemu."
#: pretix/control/templates/pretixcontrol/organizers/channel_edit.html:6
#, fuzzy
#| msgid "Sales channel"
msgid "Sales channel:"
msgstr "Kanał sprzedaży"
msgstr "Kanał sprzedaży:"
#: pretix/control/templates/pretixcontrol/organizers/channels.html:8
msgid ""
@@ -22528,18 +22493,17 @@ msgid ""
"through. This is useful to unlock new revenue streams or to separate revenue "
"between different sources for reporting purchases."
msgstr ""
"Na tej stronie można zarządzać różnymi kanałami sprzedaży biletów. Jest to "
"przydatne do odblokowania nowych źródeł przychodów lub rozdzielenia "
"przychodów między różne źródła w celu raportowania zakupów."
#: pretix/control/templates/pretixcontrol/organizers/channels.html:15
#, fuzzy
#| msgid "Add a new value"
msgid "Add a new channel"
msgstr "Dodaj nową wartość"
msgstr "Dodaj nowy kanał"
#: pretix/control/templates/pretixcontrol/organizers/channels.html:22
#, fuzzy
#| msgid "Change"
msgid "Channel"
msgstr "Zmień"
msgstr "Kanał"
#: pretix/control/templates/pretixcontrol/organizers/customer.html:7
#: pretix/control/templates/pretixcontrol/organizers/customer.html:13
@@ -26776,34 +26740,24 @@ msgid "The customer account has been anonymized."
msgstr "Konto klienta zostało zanonimizowane."
#: pretix/control/views/organizer.py:3201
#, fuzzy
#| msgid "This organizer can not be deleted."
msgid "This channel can not be deleted."
msgstr "Tego organizatora nie można usunąć."
msgstr "Tego kanału nie można usunąć."
#: pretix/control/views/organizer.py:3206
#, fuzzy
#| msgid "The selected list has been deleted."
msgid "The selected sales channel has been deleted."
msgstr "Wybrana lista została usunięta."
msgstr "Wybrany kanał sprzedaży został usunięty."
#: pretix/control/views/organizer.py:3208
#, fuzzy
#| msgid ""
#| "The team could not be deleted as some constraints (e.g. data created by "
#| "plug-ins) do not allow it."
msgid ""
"The channel could not be deleted as some constraints (e.g. data created by "
"plug-ins) did not allow it."
msgstr ""
"Zespół nie mógł zostać usunięty, ponieważ niektóre ograniczenia (np. dane "
"utworzone przez wtyczki) na to nie pozwalają."
"Kanał nie mógł zostać usunięty, ponieważ niektóre ograniczenia (np. dane "
"utworzone przez wtyczki) na to nie pozwoliły."
#: pretix/control/views/organizer.py:3232
#, fuzzy
#| msgid "The order of discounts has been updated."
msgid "The order of sales channels has been updated."
msgstr "Kolejność rabatów została zaktualizowana."
msgstr "Kolejność kanałów sprzedaży została zaktualizowana."
#: pretix/control/views/pdf.py:83
msgid "The uploaded PDF file is too large."
@@ -28613,13 +28567,6 @@ msgid "Your PayPal account has been disconnected."
msgstr "Twoje konto PayPal zostało odłączone."
#: pretix/plugins/paypal2/apps.py:40
#, fuzzy
#| msgid ""
#| "Accept payments with your PayPal account. In addition to regular PayPal "
#| "payments, you can now also offer payments in a variety of local payment "
#| "methods such as giropay, SOFORT, iDEAL and many more to your customers - "
#| "they don't even need a PayPal account. PayPal is one of the most popular "
#| "payment methods world-wide."
msgid ""
"Accept payments with your PayPal account. In addition to regular PayPal "
"payments, you can now also offer payments in a variety of local payment "
@@ -28629,9 +28576,9 @@ msgid ""
msgstr ""
"Akceptuj płatności za pomocą konta PayPal. Oprócz zwykłych płatności PayPal, "
"możesz teraz oferować swoim klientom płatności za pomocą różnych lokalnych "
"metod płatności, takich jak giropay, SOFORT, iDEAL i wiele innych - nie "
"potrzebują oni nawet konta PayPal. PayPal jest jedną z najpopularniejszych "
"metod płatności na całym świecie."
"metod płatności, takich jak eps, iDEAL i wiele innych - nie potrzebują oni "
"nawet konta PayPal. PayPal jest jedną z najpopularniejszych metod płatności "
"na całym świecie."
#: pretix/plugins/paypal2/payment.py:99
msgid "PayPal Merchant ID"
@@ -28652,14 +28599,6 @@ msgid "Alternative Payment Methods"
msgstr "Alternatywne metody płatności"
#: pretix/plugins/paypal2/payment.py:151
#, fuzzy
#| msgid ""
#| "In addition to payments through a PayPal account, you can also offer your "
#| "customers the option to pay with credit cards and other, local payment "
#| "methods such as SOFORT, giropay, iDEAL, and many more - even when they do "
#| "not have a PayPal account. Eligible payment methods will be determined "
#| "based on the shoppers location. For German merchants, this is the direct "
#| "successor of PayPal Plus."
msgid ""
"In addition to payments through a PayPal account, you can also offer your "
"customers the option to pay with credit cards and other, local payment "
@@ -28670,10 +28609,10 @@ msgid ""
msgstr ""
"Oprócz płatności za pośrednictwem konta PayPal, możesz również zaoferować "
"swoim klientom możliwość płacenia kartami kredytowymi i innymi lokalnymi "
"metodami płatności, takimi jak SOFORT, giropay, iDEAL i wiele innych - nawet "
"jeśli nie mają konta PayPal. Kwalifikujące się metody płatności zostaną "
"określone na podstawie lokalizacji kupującego. Dla niemieckich sprzedawców "
"jest to bezpośredni następca PayPal Plus."
"metodami płatności, takimi jak eps, iDEAL i wiele innych - nawet jeśli nie "
"mają konta PayPal. Kwalifikujące się metody płatności zostaną określone na "
"podstawie lokalizacji kupującego. Dla niemieckich sprzedawców jest to "
"bezpośredni następca PayPal Plus."
#: pretix/plugins/paypal2/payment.py:166
msgid "Disable SEPA Direct Debit"
@@ -29880,11 +29819,6 @@ msgid "Stripe"
msgstr "Stripe"
#: pretix/plugins/stripe/apps.py:40
#, fuzzy
#| msgid ""
#| "Accept payments via Stripe, a globally popular payment service provider. "
#| "Stripe supports payments via credit cards as well as many local payment "
#| "methods such as giropay, iDEAL, Alipay,and many more."
msgid ""
"Accept payments via Stripe, a globally popular payment service provider. "
"Stripe supports payments via credit cards as well as many local payment "
@@ -33278,6 +33212,9 @@ msgid ""
"been added to the waiting list. We will only contact you once a spot opens "
"up."
msgstr ""
"Po dodaniu do listy oczekujących <strong>nie</strong> otrzymasz wiadomość e-"
"mail z potwierdzeniem. Skontaktujemy się z Tobą dopiero, gdy zwolni się "
"miejsce."
#: pretix/presale/templates/pretixpresale/event/waitinglist.html:44
msgid "Add me to the list"
File diff suppressed because it is too large Load Diff
@@ -119,6 +119,7 @@ class CheckInListMixin(BaseExporter):
choices=[
('name', _('Attendee name')),
('code', _('Order code')),
('order_datetime', _('Order date')),
] + ([
('name:{}'.format(k), _('Attendee name: {part}').format(part=label))
for k, label, w in name_scheme['fields']
@@ -229,6 +230,8 @@ class CheckInListMixin(BaseExporter):
)
elif sort == 'code':
qs = qs.order_by(*o, 'order__code')
elif sort == 'order_datetime':
qs = qs.order_by(*o, '-order__datetime')
elif sort.startswith('name:'):
part = sort[5:]
qs = qs.annotate(
@@ -516,6 +519,7 @@ class CSVCheckinList(CheckInListMixin, ListExporter):
headers.append(_('Order time'))
headers.append(_('Requires special attention'))
headers.append(_('Comment'))
headers.append(_('Check-in text'))
headers.append(_('Seat ID'))
headers.append(_('Seat name'))
headers.append(_('Seat zone'))
@@ -623,6 +627,7 @@ class CSVCheckinList(CheckInListMixin, ListExporter):
row.append(op.order.datetime.astimezone(self.event.timezone).strftime('%H:%M:%S'))
row.append(_('Yes') if op.require_checkin_attention else _('No'))
row.append(op.order.comment or "")
row.append("\n".join(text for text in [op.order.checkin_text, op.item.checkin_text] if text))
if op.seat:
row += [
+3
View File
@@ -949,6 +949,9 @@ class PaypalMethod(BasePaymentProvider):
}
})
response = self.client.execute(req)
except KeyError:
raise PaymentException(_('Refunding the amount via PayPal failed: The original payment does not contain '
'the required information to issue an automated refund.'))
except IOError as e:
refund.order.log_action('pretix.event.order.refund.failed', {
'local_id': refund.local_id,
+1 -1
View File
@@ -163,7 +163,7 @@ def signal_process_response(sender, request: HttpRequest, response: HttpResponse
# 'frame-src': ['https://www.paypal.com', 'https://www.sandbox.paypal.com', "'nonce-{}'".format(_nonce(request))],
'frame-src': ['https:', "'nonce-{}'".format(_nonce(request))],
'connect-src': ['https://www.paypal.com', 'https://www.sandbox.paypal.com'], # Or not - seems to only affect PayPal logging...
'img-src': ['https://t.paypal.com'],
'img-src': ['https://t.paypal.com', 'https://www.paypalobjects.com'],
'style-src': ["'unsafe-inline'"] # PayPal does not comply with our nonce unfortunately, see Z#23113213
}
+1 -1
View File
@@ -1585,7 +1585,7 @@ class StripeBancontact(StripeRedirectWithAccountNamePaymentIntentMethod):
return super().payment_presale_render(payment)
class StripeSofort(StripeMethod):
class StripeSofort(StripeRedirectMethod):
identifier = 'stripe_sofort'
verbose_name = _('SOFORT via Stripe')
public_name = _('SOFORT (instant bank transfer)')
+5 -1
View File
@@ -85,7 +85,7 @@ from pretix.presale.forms.customer import AuthenticationForm, RegistrationForm
from pretix.presale.signals import (
checkout_all_optional, checkout_confirm_messages, checkout_flow_steps,
contact_form_fields, contact_form_fields_overrides,
order_meta_from_request, question_form_fields,
order_api_meta_from_request, order_meta_from_request, question_form_fields,
question_form_fields_overrides,
)
from pretix.presale.utils import customer_login
@@ -1544,11 +1544,14 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
str(m) for m in self.confirm_messages.values()
]
}
api_meta = {}
unlock_hashes = request.session.get('pretix_unlock_hashes', [])
if unlock_hashes:
meta_info['unlock_hashes'] = unlock_hashes
for receiver, response in order_meta_from_request.send(sender=request.event, request=request):
meta_info.update(response)
for receiver, response in order_api_meta_from_request.send(sender=request.event, request=request):
api_meta.update(response)
return self.do(
self.request.event.id,
@@ -1562,6 +1565,7 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
shown_total=self.cart_session.get('shown_total'),
customer=self.cart_session.get('customer'),
override_now_dt=time_machine_now(default=None),
api_meta=api_meta,
)
def get_success_message(self, value):
+8
View File
@@ -174,3 +174,11 @@ class CheckoutFieldRenderer(FieldRenderer):
else:
attrs = ''
return '<div class="{klass}"{attrs}>{html}</div>'.format(klass=self.get_form_group_class(), html=html, attrs=attrs)
def wrap_widget(self, html):
if isinstance(self.widget, CheckboxInput):
css_class = "checkbox"
if self.field.field.disabled:
css_class += " disabled"
html = f'<div class="{css_class}">{html}</div>'
return html
+12
View File
@@ -84,6 +84,7 @@ seatingframe_html_head = EventPluginSignal()
"""
Arguments: ``request``
**Temporary workaround, might be removed again later.**
This signal allows you to put code inside the HTML ``<head>`` tag
of the seatingframe page in the frontend. You will get the request as the keyword argument
``request`` and are expected to return plain HTML.
@@ -169,6 +170,17 @@ You will receive the request triggering the order creation as the ``request`` ke
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_api_meta_from_request = EventPluginSignal()
"""
Arguments: ``request``
This signal is sent before an order is created through the pretixpresale frontend. It allows you
to return a dictionary that will be merged in the api_meta attribute of the order.
You will receive the request triggering the order creation as the ``request`` keyword argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
checkout_confirm_page_content = EventPluginSignal()
"""
Arguments: ``request``
+4 -4
View File
@@ -151,7 +151,7 @@ def get_grouped_items(event, *, channel: SalesChannel, subevent=None, voucher=No
),
).filter(
variation_q,
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel.identifier),
Q(all_sales_channels=True) | Q(limit_sales_channels=channel),
active=True,
quotas__isnull=False,
subevent_disabled=False
@@ -685,7 +685,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
add_subevents_for_days(
filter_qs_by_attr(
self.request.event.subevents_annotated(
self.request.sales_channel.identifier,
self.request.sales_channel,
voucher,
).using(settings.DATABASE_REPLICA),
self.request
@@ -744,7 +744,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
add_subevents_for_days(
filter_qs_by_attr(
self.request.event.subevents_annotated(
self.request.sales_channel.identifier,
self.request.sales_channel,
voucher=voucher,
).using(settings.DATABASE_REPLICA),
self.request
@@ -793,7 +793,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
context['subevent_list'] = self.request.event.subevents_sorted(
filter_qs_by_attr(
self.request.event.subevents_annotated(
self.request.sales_channel.identifier,
self.request.sales_channel,
voucher=voucher,
).using(settings.DATABASE_REPLICA),
self.request
+2 -2
View File
@@ -179,7 +179,7 @@ class TicketPageMixin:
can_download = can_download and self.order.ticket_download_available
ctx['download_email_required'] = can_download and (
self.request.event.settings.ticket_download_require_validated_email and
self.order.sales_channel == 'web' and
self.order.sales_channel.type == 'web' and
not self.order.email_known_to_work
)
ctx['can_download'] = can_download and not ctx['download_email_required']
@@ -1106,7 +1106,7 @@ class OrderDownloadMixin:
if (
self.request.event.settings.ticket_download_require_validated_email and
self.order.sales_channel == 'web' and
self.order.sales_channel.type == 'web' and
not self.order.email_known_to_work
):
return self.error(OrderError(_('Please click the link we sent you via email to download your tickets.')))
+22 -22
View File
@@ -185,7 +185,7 @@ class EventListMixin:
def _get_event_list_queryset(self):
query = Q(is_public=True) & Q(live=True)
qs = self.request.organizer.events.using(settings.DATABASE_REPLICA).filter(query)
qs = qs.filter(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier))
qs = qs.filter(Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel))
qs = qs.annotate(
min_from=Min('subevents__date_from'),
min_to=Min('subevents__date_to'),
@@ -213,7 +213,7 @@ class EventListMixin:
).order_by('order_from')
qs = Event.annotated(filter_qs_by_attr(
qs, self.request, match_subevents_with_conditions=Q(active=True) & Q(is_public=True) & date_q
))
), self.request.sales_channel)
return qs
def _set_month_to_next_subevent(self):
@@ -724,10 +724,10 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
ctx['has_before'], ctx['has_after'] = has_before_after(
self.request.organizer.events.filter(
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
),
SubEvent.objects.filter(
Q(event__all_sales_channels=True) | Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(event__all_sales_channels=True) | Q(event__limit_sales_channels=self.request.sales_channel),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -746,14 +746,14 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
def _events_by_day(self, before, after):
ebd = defaultdict(list)
timezones = set()
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using(
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, self.request.sales_channel).using(
settings.DATABASE_REPLICA
).filter(
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
), before, after, ebd, timezones)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(event__limit_sales_channels=self.request.sales_channel),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -768,7 +768,7 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
)
)
)
)), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
), self.request.sales_channel), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
self._multiple_timezones = len(timezones) > 1
return ebd
@@ -807,11 +807,11 @@ class WeekCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
ctx['has_before'], ctx['has_after'] = has_before_after(
self.request.organizer.events.filter(
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
),
SubEvent.objects.filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(event__limit_sales_channels=self.request.sales_channel),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -842,14 +842,14 @@ class WeekCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
def _events_by_day(self, before, after):
ebd = defaultdict(list)
timezones = set()
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using(
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, self.request.sales_channel).using(
settings.DATABASE_REPLICA
).filter(
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
), before, after, ebd, timezones)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(event__limit_sales_channels=self.request.sales_channel),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -864,7 +864,7 @@ class WeekCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
)
)
)
)), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
), self.request.sales_channel), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
self._multiple_timezones = len(timezones) > 1
return ebd
@@ -946,11 +946,11 @@ class DayCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
ctx['has_before'], ctx['has_after'] = has_before_after(
self.request.organizer.events.filter(
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
),
SubEvent.objects.filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(event__limit_sales_channels=self.request.sales_channel),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -1194,14 +1194,14 @@ class DayCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
def _events_by_day(self, before, after):
ebd = defaultdict(list)
timezones = set()
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using(
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, self.request.sales_channel).using(
settings.DATABASE_REPLICA
).filter(
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
), before, after, ebd, timezones)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(event__limit_sales_channels=self.request.sales_channel),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -1216,7 +1216,7 @@ class DayCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
)
)
)
)), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
), self.request.sales_channel), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
self._multiple_timezones = len(timezones) > 1
return ebd
@@ -1229,7 +1229,7 @@ class OrganizerIcalDownload(OrganizerViewMixin, View):
filter_qs_by_attr(
self.request.organizer.events.filter(
Q(date_from__gt=cutoff) | Q(date_to__gt=cutoff),
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
is_public=True,
live=True,
has_subevents=False,
@@ -1250,7 +1250,7 @@ class OrganizerIcalDownload(OrganizerViewMixin, View):
SubEvent.objects.filter(
Q(date_from__gt=cutoff) | Q(date_to__gt=cutoff),
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(event__limit_sales_channels=self.request.sales_channel),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
+9 -9
View File
@@ -545,9 +545,9 @@ class WidgetAPIProductList(EventListMixin, View):
if hasattr(self.request, 'event'):
add_subevents_for_days(
filter_qs_by_attr(
self.request.event.subevents_annotated('web').filter(
self.request.event.subevents_annotated(self.request.sales_channel).filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
Q(event__limit_sales_channels=self.request.sales_channel),
), self.request
),
limit_before, after, ebd, set(), self.request.event,
@@ -558,8 +558,8 @@ class WidgetAPIProductList(EventListMixin, View):
add_events_for_days(
self.request,
filter_qs_by_attr(
Event.annotated(self.request.organizer.events, 'web').filter(
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
Event.annotated(self.request.organizer.events, self.request.sales_channel).filter(
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
), self.request
),
limit_before, after, ebd, timezones
@@ -572,7 +572,7 @@ class WidgetAPIProductList(EventListMixin, View):
event__live=True,
).prefetch_related(
'event___settings_objects', 'event__organizer___settings_objects'
)), self.request), limit_before, after, ebd, timezones)
), self.request.sales_channel), self.request), limit_before, after, ebd, timezones)
data['weeks'] = weeks_for_template(ebd, self.year, self.month)
for w in data['weeks']:
@@ -605,7 +605,7 @@ class WidgetAPIProductList(EventListMixin, View):
ebd = defaultdict(list)
if hasattr(self.request, 'event'):
add_subevents_for_days(
filter_qs_by_attr(self.request.event.subevents_annotated('web'), self.request),
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel), self.request),
limit_before, after, ebd, set(), self.request.event,
kwargs.get('cart_namespace')
)
@@ -613,7 +613,7 @@ class WidgetAPIProductList(EventListMixin, View):
timezones = set()
add_events_for_days(
self.request,
filter_qs_by_attr(Event.annotated(self.request.organizer.events, 'web'), self.request),
filter_qs_by_attr(Event.annotated(self.request.organizer.events, self.request.sales_channel), self.request),
limit_before, after, ebd, timezones
)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
@@ -622,7 +622,7 @@ class WidgetAPIProductList(EventListMixin, View):
event__live=True,
).prefetch_related(
'event___settings_objects', 'event__organizer___settings_objects'
)), self.request), limit_before, after, ebd, timezones)
), self.request.sales_channel), self.request), limit_before, after, ebd, timezones)
data['days'] = days_for_template(ebd, week)
for d in data['days']:
@@ -632,7 +632,7 @@ class WidgetAPIProductList(EventListMixin, View):
limit = 50
if hasattr(self.request, 'event'):
evs = filter_qs_by_attr(
self.request.event.subevents_annotated(self.request.sales_channel.identifier),
self.request.event.subevents_annotated(self.request.sales_channel),
self.request,
match_subevents_with_conditions=(
Q(Q(date_to__isnull=True) & Q(date_from__gte=now() - timedelta(hours=24)))
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -5,7 +5,7 @@
"scripts": {},
"dependencies": {
"@babel/core": "^7.24.7",
"@babel/preset-env": "^7.24.6",
"@babel/preset-env": "^7.24.7",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^15.2.3",
"vue": "^2.7.16",
@@ -195,7 +195,7 @@ input[type=number]::-webkit-outer-spin-button {
}
.alert-danger::before {
background-color: $state-danger-border;
background-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2036%2036'%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%3E%3Cpath%20d='M12.14%204.62h11.64l8.24%208.24V23.4l-8.24%208.24H12.14L3.9%2023.39V12.86l8.24-8.24Z'%20fill='%23fff'/%3E%3Cpath%20d='M24.74%2022.6c0-.28-.11-.56-.31-.76l-3.27-3.27%203.27-3.27a1.08%201.08%200%200%200%200-1.52l-1.51-1.5a1.08%201.08%200%200%200-1.52%200l-3.27%203.26-3.27-3.27a1.08%201.08%200%200%200-1.52%200l-1.5%201.51a1.08%201.08%200%200%200%200%201.52l3.26%203.27-3.27%203.27a1.08%201.08%200%200%200%200%201.52l1.51%201.51a1.08%201.08%200%200%200%201.52%200l3.27-3.27%203.27%203.27a1.08%201.08%200%200%200%201.52%200l1.51-1.51c.2-.2.31-.48.31-.76Z'%20fill='#{url-friendly-colour($state-danger-text)}'/%3E%3C/svg%3E%0A");
background-image: url('data:image/svg+xml,<svg viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" xml:space="preserve"><path d="M12.14 4.62h11.64l8.24 8.24V23.4l-8.24 8.24H12.14L3.9 23.39V12.86l8.24-8.24Zm12.6 17.98c0-.28-.11-.56-.31-.76l-3.27-3.27 3.27-3.27a1.085 1.085 0 0 0 0-1.52l-1.51-1.5a1.085 1.085 0 0 0-1.52 0l-3.27 3.26-3.27-3.27a1.085 1.085 0 0 0-1.52 0l-1.5 1.51a1.085 1.085 0 0 0 0 1.52l3.26 3.27-3.27 3.27a1.085 1.085 0 0 0 0 1.52l1.51 1.51a1.085 1.085 0 0 0 1.52 0l3.27-3.27 3.27 3.27a1.085 1.085 0 0 0 1.52 0l1.51-1.51c.2-.2.31-.48.31-.76Z" style="fill:%23fff"/></svg>');
}
.alert-primary::before {
background: $brand-primary !important;
@@ -781,7 +781,7 @@ function setup_basics(el) {
scrollTarget.id = "panel_" + $("input", scrollTarget).attr("id");
}
} else {
label = $("label", this).first().text();
label = $("label", this).first().contents().filter(function () { return this.nodeType != Node.ELEMENT_NODE || !this.classList.contains("optional") }).text();
description = $(".help-block", this).first().text();
scrollTarget = $(":input", this).get(0);
}
@@ -767,9 +767,11 @@ h1 .label {
.plugin-container {
flex-basis: 100%;
flex-shrink: 0;
border-top: 1px solid #ccc;
padding-top: 15px;
}
.plugin-container:not(.featured-plugin) + .plugin-container {
border-top: 1px solid #ccc;
}
h4 {
margin-top: 0;
}
+20 -15
View File
@@ -380,23 +380,28 @@ $(function () {
" #id_city, #id_country, #id_state").change(function () {
if (copy_to_first_ticket) {
var $first_ticket_form = $(".questions-form").first().find("[data-addonidx=0]");
$first_ticket_form.find("input[id*=attendee_email]").val($("#id_email").val());
$first_ticket_form.find("input[id$=company]").val($("#id_company").val());
$first_ticket_form.find("textarea[id$=street]").val($("#id_street").val());
$first_ticket_form.find("input[id$=zipcode]").val($("#id_zipcode").val());
$first_ticket_form.find("input[id$=city]").val($("#id_city").val());
$first_ticket_form.find("select[id$=state]").val($("#id_state").val());
if ($first_ticket_form.find("select[id$=country]").val() !== $("#id_country").val()) {
$first_ticket_form.find("select[id$=country]").val($("#id_country").val()).trigger('change');
$first_ticket_form.find("[id$=" + this.id.substring(3) + "]").val(this.value);
if (this.placeholder) {
$first_ticket_form.find("[placeholder='" + this.placeholder + "']").val(this.value);
}
var label = $("label[for=" + this.id +"]").first().contents().filter(function () {
return this.nodeType != Node.ELEMENT_NODE || !this.classList.contains("sr-only");
}).text().trim();
if (label) {
// match to placeholder and label
$first_ticket_form.find("[placeholder='" + label + "']").val(this.value);
var v = this.value;
$first_ticket_form.find("label").each(function() {
var text = $(this).first().contents().filter(function () {
return this.nodeType != Node.ELEMENT_NODE || !this.classList.contains("sr-only");
}).text().trim();
if (text == label) {
$("#" + this.getAttribute("for")).val(v);
}
});
}
$first_ticket_form.find("[id*=attendee_name_parts]").each(function () {
var parts = $(this).attr("id").split("_");
var num = parts[parts.length - 1];
$(this).val($("#id_name_parts_" + num).val());
});
}
});
}).trigger("change");
attendee_address_fields.change(function () {
copy_to_first_ticket = false;
});
+1
View File
@@ -33,6 +33,7 @@ filterwarnings =
ignore::DeprecationWarning:django
ignore::DeprecationWarning:cgi
ignore::DeprecationWarning:vat_moss
ignore::cryptography.utils.CryptographyDeprecationWarning:pypdf
ignore:.*ast.NameConstant.*:DeprecationWarning:reportlab
ignore:.*utcnow.*:DeprecationWarning:
ignore:.*PyType_Spec.*:DeprecationWarning:
+15
View File
@@ -1381,3 +1381,18 @@ def test_checkin_pdf_data_requires_permission(token_client, event, team, organiz
organizer.slug, event.slug, clist_all.pk
))
assert not resp.data['results'][0].get('pdf_data')
@pytest.mark.django_db
def test_expand(token_client, organizer, event, clist, clist_all, item, other_item, order, django_assert_max_num_queries):
with scopes_disabled():
op = order.positions.first()
var1 = item.variations.create(value="XS")
op.variation = var1
op.save()
resp = token_client.get('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/?search=z3fsn8jyu&expand=variation'.format(
organizer.slug, event.slug, clist_all.pk
))
assert resp.status_code == 200
assert 'value' in resp.data['results'][0]['variation']
+44
View File
@@ -763,6 +763,50 @@ def test_event_update(token_client, organizer, event, item, meta_prop):
assert cnt == event.all_logentries().count()
@pytest.mark.django_db
def test_event_update_plugins_validation(token_client, organizer, event, item, meta_prop):
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
"plugins": ["pretix.plugins.paypal2", "unknown"]
},
format='json'
)
assert resp.status_code == 400
assert resp.data == {"plugins": ["Unknown plugin: 'unknown'."]}
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
"plugins": ["pretix.plugins.paypal2", "tests.testdummyhidden"]
},
format='json'
)
assert resp.status_code == 400
assert resp.data == {"plugins": ["Unknown plugin: 'tests.testdummyhidden'."]}
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
"plugins": ["pretix.plugins.paypal2", "tests.testdummyrestricted"]
},
format='json'
)
assert resp.status_code == 400
assert resp.data == {"plugins": ["Restricted plugin: 'tests.testdummyrestricted'."]}
organizer.settings.allowed_restricted_plugins = ["tests.testdummyrestricted"]
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
"plugins": ["pretix.plugins.paypal2", "tests.testdummyrestricted"]
},
format='json'
)
assert resp.status_code == 200
@pytest.mark.django_db
def test_event_test_mode(token_client, organizer, event):
resp = token_client.patch(
+12
View File
@@ -86,6 +86,18 @@ def test_giftcard_list(token_client, organizer, event, giftcard, other_giftcard)
assert resp.status_code == 200
assert 2 == len(resp.data['results'])
resp = token_client.get('/api/v1/organizers/{}/giftcards/?expired=false'.format(organizer.slug))
assert 1 == len(resp.data['results'])
resp = token_client.get('/api/v1/organizers/{}/giftcards/?expired=true'.format(organizer.slug))
assert 0 == len(resp.data['results'])
resp = token_client.get('/api/v1/organizers/{}/giftcards/?value=23.00'.format(organizer.slug))
assert 1 == len(resp.data['results'])
resp = token_client.get('/api/v1/organizers/{}/giftcards/?value=23'.format(organizer.slug))
assert 1 == len(resp.data['results'])
resp = token_client.get('/api/v1/organizers/{}/giftcards/?value=24'.format(organizer.slug))
assert 0 == len(resp.data['results'])
@pytest.mark.django_db
def test_giftcard_detail(token_client, organizer, event, giftcard):
+46 -1
View File
@@ -370,6 +370,13 @@ def test_item_list(token_client, organizer, event, team, item):
assert resp.status_code == 200
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?search=Budget'.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?search=Free'.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert [] == resp.data['results']
@pytest.mark.django_db
def test_item_detail(token_client, organizer, event, team, item):
@@ -590,7 +597,28 @@ def test_item_create_with_variation(token_client, organizer, event, item, catego
"meta_data": {
"day": "Wednesday",
},
}
},
{
"value": {
"de": "web",
"en": "web"
},
"active": True,
"require_approval": True,
"checkin_attention": False,
"checkin_text": None,
"require_membership": False,
"require_membership_hidden": False,
"require_membership_types": [],
"description": None,
"position": 0,
"default_price": None,
"sales_channels": ["web"],
"price": "23.00",
"meta_data": {
"day": "Wednesday",
},
},
]
},
format='json'
@@ -604,6 +632,8 @@ def test_item_create_with_variation(token_client, organizer, event, item, catego
assert new_item.variations.first().all_sales_channels is True
assert not new_item.variations.first().limit_sales_channels.exists()
assert new_item.variations.first().meta_data == {"day": "Wednesday"}
assert new_item.variations.last().all_sales_channels is False
assert new_item.variations.last().limit_sales_channels.exists()
@pytest.mark.django_db
@@ -1389,6 +1419,21 @@ def test_variations_list(token_client, organizer, event, item, variation):
assert res['position'] == resp.data['results'][0]['position']
assert res['price'] == resp.data['results'][0]['price']
resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/variations/?active=true'.format(organizer.slug, event.slug, item.pk))
assert resp.status_code == 200
assert res['value'] == resp.data['results'][0]['value']
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/items/{}/variations/?active=false'.format(organizer.slug, event.slug, item.pk))
assert resp.status_code == 200
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/variations/?search=Child'.format(organizer.slug, event.slug, item.pk))
assert resp.status_code == 200
assert res['value'] == resp.data['results'][0]['value']
resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/variations/?search=Incorrect'.format(organizer.slug, event.slug, item.pk))
assert resp.status_code == 200
assert [] == resp.data['results']
@pytest.mark.django_db
def test_variations_detail(token_client, organizer, event, item, variation):
+6
View File
@@ -255,6 +255,9 @@ def test_order_update_allowed_fields(token_client, organizer, event, order):
organizer.slug, event.slug, order.code
), format='json', data={
'comment': 'Here is a comment',
'api_meta': {
'test': 1
},
'valid_if_pending': True,
'custom_followup_at': '2021-06-12',
'checkin_attention': True,
@@ -280,6 +283,9 @@ def test_order_update_allowed_fields(token_client, organizer, event, order):
assert resp.status_code == 200
order.refresh_from_db()
assert order.comment == 'Here is a comment'
assert order.api_meta == {
'test': 1
}
assert order.custom_followup_at.isoformat() == '2021-06-12'
assert order.checkin_attention
assert order.checkin_text == 'foobar'
+7
View File
@@ -232,6 +232,9 @@ def test_order_create(token_client, organizer, event, item, quota, question):
with scopes_disabled():
customer = organizer.customers.create()
res['customer'] = customer.identifier
res['api_meta'] = {
'test': 1
}
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/orders/'.format(
organizer.slug, event.slug
@@ -251,6 +254,9 @@ def test_order_create(token_client, organizer, event, item, quota, question):
assert o.valid_if_pending
assert o.expires > now()
assert not o.testmode
assert o.api_meta == {
'test': 1
}
with scopes_disabled():
p = o.payments.first()
@@ -421,6 +427,7 @@ def test_order_create_simulate(token_client, organizer, event, item, quota, ques
],
'total': '21.75',
'comment': '',
'api_meta': {},
"custom_followup_at": None,
'invoice_address': {
'is_business': False,
+41
View File
@@ -291,6 +291,7 @@ TEST_ORDER_RES = {
"payment_provider": "banktransfer",
"total": "23.00",
"comment": "",
"api_meta": {},
"custom_followup_at": None,
"checkin_attention": False,
"checkin_text": None,
@@ -414,6 +415,16 @@ def test_order_list(token_client, organizer, event, order, item, taxrule, questi
'/api/v1/organizers/{}/events/{}/orders/?email=foo@example.org'.format(organizer.slug, event.slug))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?payment_provider=banktransfer'.format(organizer.slug, event.slug))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?payment_provider=manual'.format(organizer.slug, event.slug))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?sales_channel=web'.format(organizer.slug, event.slug))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?sales_channel=bar'.format(organizer.slug, event.slug))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?locale=en'.format(organizer.slug, event.slug))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?locale=de'.format(organizer.slug, event.slug))
@@ -434,6 +445,36 @@ def test_order_list(token_client, organizer, event, order, item, taxrule, questi
))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_since={}'.format(
organizer.slug, event.slug,
(order.datetime - datetime.timedelta(hours=1)).isoformat().replace('+00:00', 'Z')
))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_since={}'.format(
organizer.slug, event.slug, order.datetime.isoformat().replace('+00:00', 'Z')
))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_since={}'.format(
organizer.slug, event.slug,
(order.datetime + datetime.timedelta(hours=1)).isoformat().replace('+00:00', 'Z')
))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_before={}'.format(
organizer.slug, event.slug,
(order.datetime - datetime.timedelta(hours=1)).isoformat().replace('+00:00', 'Z')
))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_before={}'.format(
organizer.slug, event.slug, order.datetime.isoformat().replace('+00:00', 'Z')
))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_before={}'.format(
organizer.slug, event.slug,
(order.datetime + datetime.timedelta(hours=1)).isoformat().replace('+00:00', 'Z')
))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?include_canceled_positions=false'.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert len(resp.data['results'][0]['positions']) == 1
+5
View File
@@ -60,6 +60,11 @@ def test_hook_list(token_client, organizer, event, webhook):
assert resp.status_code == 200
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/webhooks/?enabled=true'.format(organizer.slug))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/webhooks/?enabled=false'.format(organizer.slug))
assert [] == resp.data['results']
@pytest.mark.django_db
def test_hook_detail(token_client, organizer, event, webhook):
+32 -30
View File
@@ -2329,8 +2329,8 @@ class EventTest(TestCase):
item2 = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=False)
q.items.add(item)
q.items.add(item2)
assert Event.annotated(Event.objects).first().active_quotas == [q]
assert Event.annotated(Event.objects, 'foo').first().active_quotas == []
assert Event.annotated(Event.objects, 'web').first().active_quotas == [q]
assert Event.annotated(Event.objects, self.organizer.sales_channels.get(identifier="bar")).first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_product_inactive(self):
@@ -2341,7 +2341,7 @@ class EventTest(TestCase):
q = Quota.objects.create(event=event, name='Quota', size=2)
item = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=False)
q.items.add(item)
assert Event.annotated(Event.objects).first().active_quotas == []
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_product_hidden_by_voucher(self):
@@ -2354,16 +2354,16 @@ class EventTest(TestCase):
q.items.add(item)
voucher = Voucher.objects.create(event=event, code='a', item=item, show_hidden_items=True)
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q]
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q]
voucher = Voucher.objects.create(event=event, code='b', item=item, show_hidden_items=False)
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == []
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == []
voucher = Voucher.objects.create(event=event, code='c', show_hidden_items=True)
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q]
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q]
voucher = Voucher.objects.create(event=event, code='d', quota=q, show_hidden_items=True)
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q]
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q]
item2 = Item.objects.create(event=event, name='Early-bird ticket', default_price=0)
var = item2.variations.create(item=item2, value='Test', hide_without_voucher=True)
@@ -2373,13 +2373,13 @@ class EventTest(TestCase):
q.variations.add(var)
voucher = Voucher.objects.create(event=event, code='e', item=item2, variation=var, show_hidden_items=True)
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q]
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q]
voucher = Voucher.objects.create(event=event, code='f', item=item2, variation=var2, show_hidden_items=True)
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == []
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == []
voucher = Voucher.objects.create(event=event, code='g', quota=q, show_hidden_items=True)
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q]
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q]
@classscope(attr='organizer')
def test_active_quotas_annotation_product_addon(self):
@@ -2395,7 +2395,7 @@ class EventTest(TestCase):
item.category = cat
item.save()
q.items.add(item)
assert Event.annotated(Event.objects).first().active_quotas == []
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_product_unavailable(self):
@@ -2407,7 +2407,7 @@ class EventTest(TestCase):
item = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=True,
available_until=now() - timedelta(days=1))
q.items.add(item)
assert Event.annotated(Event.objects).first().active_quotas == []
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_variation_not_in_quota(self):
@@ -2419,7 +2419,7 @@ class EventTest(TestCase):
item = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=True)
item.variations.create(value="foo")
q.items.add(item)
assert Event.annotated(Event.objects).first().active_quotas == []
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_variation(self):
@@ -2434,29 +2434,29 @@ class EventTest(TestCase):
v.limit_sales_channels.add(self.organizer.sales_channels.get(identifier="web"))
q.items.add(item)
q.variations.add(v)
assert Event.annotated(Event.objects).first().active_quotas == [q]
assert Event.annotated(Event.objects, 'web').first().active_quotas == [q]
item.available_until = now() - timedelta(days=1)
item.save()
assert Event.annotated(Event.objects).first().active_quotas == []
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
item.available_until = None
item.available_from = now() + timedelta(days=1)
item.save()
assert Event.annotated(Event.objects).first().active_quotas == []
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
item.available_until = None
item.available_from = None
item.active = False
item.save()
assert Event.annotated(Event.objects).first().active_quotas == []
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
item.active = True
item.save()
assert Event.annotated(Event.objects).first().active_quotas == [q]
assert Event.annotated(Event.objects, 'foo').first().active_quotas == []
assert Event.annotated(Event.objects, 'web').first().active_quotas == [q]
assert Event.annotated(Event.objects, self.organizer.sales_channels.get(identifier="bar")).first().active_quotas == []
v.active = False
v.save()
assert Event.annotated(Event.objects).first().active_quotas == []
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
item.hide_without_voucher = True
item.save()
assert Event.annotated(Event.objects).first().active_quotas == []
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
class SubEventTest(TestCase):
@@ -2502,8 +2502,10 @@ class SubEventTest(TestCase):
all_sales_channels=False)
item.limit_sales_channels.add(self.organizer.sales_channels.get(identifier="web"))
q.items.add(item)
assert SubEvent.annotated(SubEvent.objects).first().active_quotas == [q]
assert SubEvent.annotated(SubEvent.objects, 'foo').first().active_quotas == []
assert SubEvent.annotated(SubEvent.objects, 'web').first().active_quotas == [q]
assert SubEvent.annotated(SubEvent.objects, 'bar').first().active_quotas == []
assert SubEvent.annotated(SubEvent.objects, self.organizer.sales_channels.get(identifier="web")).first().active_quotas == [q]
assert SubEvent.annotated(SubEvent.objects, self.organizer.sales_channels.get(identifier="bar")).first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_no_interference(self):
@@ -2514,8 +2516,8 @@ class SubEventTest(TestCase):
subevent=se2)
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=0, active=True)
q.items.add(item)
assert SubEvent.annotated(SubEvent.objects).filter(pk=self.se.pk).first().active_quotas == []
assert SubEvent.annotated(SubEvent.objects).filter(pk=se2.pk).first().active_quotas == [q]
assert SubEvent.annotated(SubEvent.objects, 'web').filter(pk=self.se.pk).first().active_quotas == []
assert SubEvent.annotated(SubEvent.objects, 'web').filter(pk=se2.pk).first().active_quotas == [q]
@classscope(attr='organizer')
def test_best_availability(self):
@@ -2540,7 +2542,7 @@ class SubEventTest(TestCase):
q = Quota.objects.create(event=self.event, name='Quota', size=1,
subevent=self.se)
q.items.add(item)
obj = SubEvent.annotated(SubEvent.objects).first()
obj = SubEvent.annotated(SubEvent.objects, 'web').first()
assert len(obj.active_quotas) == 1
assert obj.best_availability == (Quota.AVAILABILITY_GONE, 0, 1)
@@ -2548,14 +2550,14 @@ class SubEventTest(TestCase):
q2 = Quota.objects.create(event=self.event, name='Quota 2', size=2,
subevent=self.se)
q2.items.add(item)
obj = SubEvent.annotated(SubEvent.objects).first()
obj = SubEvent.annotated(SubEvent.objects, 'web').first()
assert len(obj.active_quotas) == 2
assert obj.best_availability == (Quota.AVAILABILITY_GONE, 0, 1)
# 2 quotas - 2 items. Higher quota wins since second item is only connected to second quota.
item2 = Item.objects.create(event=self.event, name='Regular ticket', default_price=10, active=True)
q2.items.add(item2)
obj = SubEvent.annotated(SubEvent.objects).first()
obj = SubEvent.annotated(SubEvent.objects, 'web').first()
assert len(obj.active_quotas) == 2
assert obj.best_availability == (Quota.AVAILABILITY_OK, 1, 2)
assert obj.best_availability_is_low
@@ -2564,7 +2566,7 @@ class SubEventTest(TestCase):
q.size = 10
q.save()
q2.delete()
obj = SubEvent.annotated(SubEvent.objects).first()
obj = SubEvent.annotated(SubEvent.objects, 'web').first()
assert len(obj.active_quotas) == 1
assert obj.best_availability == (Quota.AVAILABILITY_OK, 9, 10)
assert not obj.best_availability_is_low
@@ -2572,7 +2574,7 @@ class SubEventTest(TestCase):
# Unlimited quota
q.size = None
q.save()
obj = SubEvent.annotated(SubEvent.objects).first()
obj = SubEvent.annotated(SubEvent.objects, 'web').first()
assert obj.best_availability == (Quota.AVAILABILITY_OK, None, None)
assert not obj.best_availability_is_low
+19
View File
@@ -299,6 +299,8 @@ class EventsTest(SoupTest):
doc = self.get_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug))
self.assertIn("Stripe", doc.select(".form-plugins")[0].text)
self.assertIn("Enable", doc.select("[name=\"plugin:pretix.plugins.stripe\"]")[0].text)
assert not doc.select("[name=\"plugin:tests.testdummyrestricted\"]")
assert not doc.select("[name=\"plugin:tests.testdummyhidden\"]")
doc = self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
{'plugin:pretix.plugins.stripe': 'enable'})
@@ -308,6 +310,23 @@ class EventsTest(SoupTest):
{'plugin:pretix.plugins.stripe': 'disable'})
self.assertIn("Enable", doc.select("[name=\"plugin:pretix.plugins.stripe\"]")[0].text)
self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
{'plugin:tests.testdummyhidden': 'enable'})
self.event1.refresh_from_db()
assert "testdummyhidden" not in self.event1.plugins
self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
{'plugin:tests.testdummyrestricted': 'enable'})
self.event1.refresh_from_db()
assert "testdummyrestricted" not in self.event1.plugins
self.orga1.settings.allowed_restricted_plugins = ["tests.testdummyrestricted"]
self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
{'plugin:tests.testdummyrestricted': 'enable'})
self.event1.refresh_from_db()
assert "testdummyrestricted" in self.event1.plugins
def test_testmode_enable(self):
self.event1.testmode = False
self.event1.save()
+63 -18
View File
@@ -91,12 +91,12 @@ def test_csv_simple(event):
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title","Attendee name:
First name","Attendee name: Middle name","Attendee name: Family name","Product","Price","Checked in","Checked out","Automatically
checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special attention",
"Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until","Address","ZIP code",
"Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until","Address","ZIP code",
"City","Country","State"
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","","No","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
""")
@@ -113,12 +113,12 @@ def test_csv_order_by_name_parts(event): # noqa
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Checked out","Automatically checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special
attention","Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
attention","Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
"Address","ZIP code","City","Country","State"
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","","No","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
""")
c = CSVCheckinList(event, organizer=event.organizer)
_, _, content = c.render({
@@ -131,12 +131,12 @@ def test_csv_order_by_name_parts(event): # noqa
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Checked out","Automatically checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special
attention","Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
attention","Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
"Address","ZIP code","City","Country","State"
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","","No","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
""")
@@ -184,12 +184,12 @@ def test_csv_order_by_inherited_name_parts(event): # noqa
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Checked out","Automatically checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special
attention","Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
attention","Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
"Address","ZIP code","City","Country","State"
"BAR","Mr Albert J Zulu","Mr","Albert","J","Zulu","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytyy",
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"FOO","Mr Paul A Jones","Mr","Paul","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
""")
c = CSVCheckinList(event, organizer=event.organizer)
_, _, content = c.render({
@@ -202,12 +202,12 @@ def test_csv_order_by_inherited_name_parts(event): # noqa
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Checked out","Automatically checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special
attention","Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
attention","Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
"Address","ZIP code","City","Country","State"
"BAR","Mr Albert J Zulu","Mr","Albert","J","Zulu","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytyy",
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"FOO","Mr Paul A Jones","Mr","Paul","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
""")
c = CSVCheckinList(event, organizer=event.organizer)
_, _, content = c.render({
@@ -220,10 +220,55 @@ def test_csv_order_by_inherited_name_parts(event): # noqa
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Checked out","Automatically checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special
attention","Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
attention","Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
"Address","ZIP code","City","Country","State"
"FOO","Mr Paul A Jones","Mr","Paul","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"BAR","Mr Albert J Zulu","Mr","Albert","J","Zulu","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytyy",
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
""")
@pytest.mark.django_db
def test_csv_order_by_orderdatetime(event):
order1 = event.orders.first()
order1.checkin_text = 'meow'
order1.save()
order2 = Order.objects.create(
code='FOO2', event=event, email='dummy@dummy.test', phone="+498912345678",
status=Order.STATUS_PAID,
datetime=datetime.datetime(2019, 2, 22, 22, 0, 0, tzinfo=datetime.timezone.utc),
expires=now() + datetime.timedelta(days=10),
total=33, locale='en', checkin_text='beep',
sales_channel=event.organizer.sales_channels.get(identifier="web"),
)
item_ticket = Item.objects.create(event=event, name="Ticket2", default_price=23, admission=True, checkin_text='boop')
OrderPosition.objects.create(
order=order2,
item=item_ticket,
variation=None,
price=Decimal("23"),
attendee_name_parts={"title": "Mx", "given_name": "Alex", "middle_name": "F", "family_name": "Nord"},
secret='asdfasdfasdfasdfasdfasdfasfdasdf'
)
c = CSVCheckinList(event, organizer=event.organizer)
_, _, content = c.render({
'list': event.checkin_lists.first().pk,
'secrets': True,
'sort': 'order_datetime',
'_format': 'default',
'questions': []
})
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title","Attendee name:
First name","Attendee name: Middle name","Attendee name: Family name","Product","Price","Checked in","Checked out","Automatically
checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special attention",
"Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until","Address","ZIP code",
"City","Country","State"
"FOO2","Mx Alex F Nord","Mx","Alex","F","Nord","Ticket2","23.00","","","No","asdfasdfasdfasdfasdfasdfasfdasdf",
"dummy@dummy.test","'+498912345678","","","2019-02-22","22:00:00","No","","beep\nboop","","","","","","","","","","","","",""
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","meow","","","","","","","","","","","","",""
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","","No","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","meow","","","","","","","","","","","","",""
""")
+2
View File
@@ -28,6 +28,8 @@ TEST_DIR = os.path.dirname(__file__)
TEMPLATES[0]['DIRS'].append(os.path.join(TEST_DIR, 'templates')) # NOQA
INSTALLED_APPS.append('tests.testdummy') # NOQA
INSTALLED_APPS.append('tests.testdummyrestricted') # NOQA
INSTALLED_APPS.append('tests.testdummyhidden') # NOQA
PRETIX_AUTH_BACKENDS = [
'pretix.base.auth.NativeAuthBackend',
+21
View File
@@ -0,0 +1,21 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io 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/>.
#
+35
View File
@@ -0,0 +1,35 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io 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 django.apps import AppConfig
class TestDummyHiddenApp(AppConfig):
name = 'tests.testdummyhidden'
verbose_name = 'testdummyhidden'
class PretixPluginMeta:
name = 'testdummyhidden'
version = '1.0.0'
restricted = True
def is_available(self, event):
return False
+21
View File
@@ -0,0 +1,21 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io 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/>.
#
+32
View File
@@ -0,0 +1,32 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io 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 django.apps import AppConfig
class TestDummyRestrictedApp(AppConfig):
name = 'tests.testdummyrestricted'
verbose_name = 'testdummyrestricted'
class PretixPluginMeta:
name = 'testdummyrestricted'
version = '1.0.0'
restricted = True