Compare commits

...

85 Commits

Author SHA1 Message Date
Raphael Michel
fa8b1c176b Bump to 4.3.0 2021-09-29 11:17:12 +02:00
Richard Schreiber
2598787602 Customer profiles: add minor improvements around disabled fields and margins (#2195) 2021-09-29 10:34:45 +02:00
Raphael Michel
003fa62996 Fix help text 2021-09-28 18:07:34 +02:00
Raphael Michel
798c21955e Translated on translate.pretix.eu (German)
Currently translated at 100.0% (4413 of 4413 strings)

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

powered by weblate
2021-09-27 21:52:58 +02:00
Raphael Michel
fe6185af4b Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4413 of 4413 strings)

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

powered by weblate
2021-09-27 21:52:58 +02:00
Raphael Michel
7bacefa442 Update spelling wordlist 2021-09-27 21:47:05 +02:00
Raphael Michel
04e187c297 Update po files
[CI skip]

Signed-off-by: Raphael Michel <mail@raphaelmichel.de>
2021-09-27 20:49:18 +02:00
Raphael Michel
9f2ffc3276 Improvements around the waiting list (#2219)
* Waiting list: Support for seated events, pre-fill customer email address

* Allow people to remove themselves

* Update src/pretix/base/settings.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/control/views/waitinglist.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/control/views/waitinglist.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/control/views/waitinglist.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

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

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Resolve a review note

* Review notes

* Fix linter issues

* Fix import

Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-09-27 20:48:02 +02:00
Diego Rodrigo
a9a4cf6fca Translated on translate.pretix.eu (Portuguese (Brazil))
Currently translated at 14.5% (641 of 4403 strings)

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

powered by weblate
2021-09-27 20:26:20 +02:00
ofirtro
a563316e22 Translated on translate.pretix.eu (Hebrew)
Currently translated at 20.4% (35 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/he/

powered by weblate
2021-09-27 20:26:20 +02:00
ofirtro
4b6f55c31d Added translation on translate.pretix.eu (Hebrew) 2021-09-27 20:26:20 +02:00
Klevagruva
21a8fad17a Translated on translate.pretix.eu (Swedish)
Currently translated at 17.0% (749 of 4403 strings)

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

powered by weblate
2021-09-27 20:26:20 +02:00
Raphael Michel
7586df9d3f Docs: Fix note on widget on mobile 2021-09-27 11:34:55 +02:00
Raphael Michel
1d4afa5d27 Sendmail: Fix invalid state if attachment is adde then removed 2021-09-23 17:36:33 +02:00
Richard Schreiber
720d9b924e [Fix] on rtl-languages float productpicture to the right (#2224) 2021-09-23 17:22:50 +02:00
Raphael Michel
9f56669f2a Truelink filter: Allow dots with spaces 2021-09-23 09:50:39 +02:00
Richard Schreiber
fc541016c6 fix typo in logo-image-settings 2021-09-23 08:28:39 +02:00
Raphael Michel
5eefe9ad1e Fix linter issues 2021-09-20 16:51:48 +02:00
Raphael Michel
1d065a7672 Add setting organizer_logo_image_inherit 2021-09-17 13:33:34 +02:00
Raphael Michel
101f5f7781 Rewrite default ticket PDF to make sure caches affected by previous bug are cleaned 2021-09-17 11:55:53 +02:00
Raphael Michel
af7c6d360f Partially revert migration command monkeypatching 2021-09-17 11:07:46 +02:00
Raphael Michel
8751e6e5ba Product list: Show "sold out" before expanding variations 2021-09-17 10:20:43 +02:00
Raphael Michel
93004a8125 Customer detailv iew: Do not show names as "None" 2021-09-17 10:20:43 +02:00
Raphael Michel
adf40e1d56 Refactor our migrate command monkeypatching 2021-09-17 10:20:43 +02:00
Raphael Michel
364cfe0131 Update nginx config for static files 2021-09-17 10:20:43 +02:00
Raphael Michel
1514527ef3 Consistent naming 2021-09-17 10:20:43 +02:00
Tim Neumann
680024234d Dockerfile: Move nginx client_max_body_size to seperate file (#2207) 2021-09-16 12:36:25 +02:00
Richard Schreiber
2a3660f2d1 Fix -- copy answers even when matching customer profiles exist (#2209) 2021-09-16 10:07:43 +02:00
Richard Schreiber
2041d1213a fix address expand button submit-bug 2021-09-16 09:10:20 +02:00
Raphael Michel
42a1fe9bd1 Event settings API: Fix setting confirm_texts 2021-09-15 16:28:57 +02:00
Raphael Michel
002469d523 Fix .po file 2021-09-15 14:47:58 +02:00
pretix translation bot
5be4af1305 Translations update from Weblate (#2205)
* Translated on translate.pretix.eu (German)

Currently translated at 100.0% (4403 of 4403 strings)

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

powered by weblate

* Translated on translate.pretix.eu (German (informal) (de_Informal))

Currently translated at 100.0% (4403 of 4403 strings)

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

powered by weblate

Co-authored-by: Raphael Michel <michel@rami.io>
2021-09-15 13:54:39 +02:00
Raphael Michel
0b241438e1 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2021-09-15 13:44:42 +02:00
Raphael Michel
61649ab2b8 Self-service cancellation: Allow to disable auto-refunds 2021-09-15 13:43:55 +02:00
Mohamed Tawfiq
848ea999c5 Translated on translate.pretix.eu (Arabic)
Currently translated at 96.4% (165 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ar/

powered by weblate
2021-09-15 13:43:52 +02:00
Mohamed Tawfiq
dfa82870fb Translated on translate.pretix.eu (Arabic)
Currently translated at 88.6% (3891 of 4391 strings)

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

powered by weblate
2021-09-15 13:43:52 +02:00
Mohamed Tawfiq
e05ac7ef34 Translated on translate.pretix.eu (Arabic)
Currently translated at 88.6% (3891 of 4391 strings)

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

powered by weblate
2021-09-15 13:43:52 +02:00
Raphael Michel
ad2334bffc WebHookCall: Increase max URL size 2021-09-15 13:06:25 +02:00
Raphael Michel
17adde99fa Allow to restrict availability of variations by date, sales channel, and voucher (#2202) 2021-09-15 12:04:17 +02:00
Raphael Michel
4789d82c4e Shredder: Fix crash in AttendeeInfoShredder 2021-09-14 15:28:39 +02:00
Raphael Michel
0567e2d22b API: Fix crash on missing require_membership_types property 2021-09-14 15:28:02 +02:00
Raphael Michel
2e0592b0a6 API: Fix crash on invalid input (PRETIXEU-5A9) 2021-09-14 15:02:06 +02:00
Mie Frydensbjerg
7f6d234b4c Translated on translate.pretix.eu (Danish)
Currently translated at 59.0% (101 of 171 strings)

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

powered by weblate
2021-09-13 12:00:03 +02:00
Mie Frydensbjerg
0436de316b Translated on translate.pretix.eu (Danish)
Currently translated at 36.4% (1600 of 4391 strings)

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

powered by weblate
2021-09-13 12:00:03 +02:00
Klevagruva
e16d643d2a Translated on translate.pretix.eu (Swedish)
Currently translated at 16.6% (730 of 4391 strings)

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

powered by weblate
2021-09-13 12:00:03 +02:00
Raphael Michel
bdec22cf3b Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4391 of 4391 strings)

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

powered by weblate
2021-09-13 12:00:03 +02:00
Raphael Michel
b38df27dce Order import: Fix handling of seat IDs 2021-09-09 18:14:43 +02:00
Tim Neumann
b95f556d8f Add config options for max file upload sizes (#2199)
* feat(config): Add config options for max file upload sizes

Closes #2198

* Apply suggestions from code review

Fix docs and comment in settings.py

Co-authored-by: Richard Schreiber <wiffbi@gmail.com>

* Fix import order using isort

Co-authored-by: Richard Schreiber <wiffbi@gmail.com>
2021-09-09 15:55:06 +02:00
Raphael Michel
851a4c977c Fix inconsistent handling of all_optional 2021-09-08 20:43:56 +02:00
Raphael Michel
7bffd461d1 Allow sales channels to opt out of customer accounts 2021-09-08 20:33:18 +02:00
Richard Schreiber
9a3b4f7863 Subevent: fix overflow for long lines in location 2021-09-08 13:22:57 +02:00
Raphael Michel
673a38ddc8 Cart: Display subevent location and end time in cart (#2191) 2021-09-08 11:24:39 +02:00
Richard Schreiber
a27b8bf213 Subevent: add missing verbose_name for seating plan (#2194) 2021-09-07 09:09:16 +02:00
Raphael Michel
36e6f10b37 Check-in list rule visualization: Fix broken height calculation 2021-09-06 22:35:14 +02:00
Raphael Michel
fde10d7f55 Fix missing license header 2021-09-06 21:14:25 +02:00
Raphael Michel
6b44b2f429 Translated on translate.pretix.eu (German)
Currently translated at 100.0% (4391 of 4391 strings)

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

powered by weblate
2021-09-06 21:14:11 +02:00
Raphael Michel
5e9018e0fd Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4391 of 4391 strings)

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

powered by weblate
2021-09-06 21:14:11 +02:00
Raphael Michel
185f8066ae Fix incorrect part of previous commit 2021-09-06 20:58:40 +02:00
Raphael Michel
6388f7b29c Fix #2192 -- Invoice address name-field always gets overwritten with customer profile 2021-09-06 20:57:45 +02:00
Raphael Michel
4aa2c9d51d Update po files
[CI skip]

Signed-off-by: Raphael Michel <mail@raphaelmichel.de>
2021-09-06 20:51:34 +02:00
Raphael Michel
ef9256f0b0 Fix typo in cache key 2021-09-06 20:50:47 +02:00
Raphael Michel
28d78e40f9 Allow to save invoice addresses and attendee profiles to customer account (#2084)
Co-authored-by: Raphael Michel <michel@rami.io>
Co-authored-by: Richard Schreiber <wiffbi@gmail.com>
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-09-06 20:50:25 +02:00
Klevagruva
89554a82eb Translated on translate.pretix.eu (Swedish)
Currently translated at 16.2% (708 of 4364 strings)

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

powered by weblate
2021-09-06 20:18:55 +02:00
Klevagruva
ae99e82ad1 Translated on translate.pretix.eu (Swedish)
Currently translated at 15.1% (663 of 4364 strings)

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

powered by weblate
2021-09-06 20:18:55 +02:00
rauxenz
5ea3d01b8d Translated on translate.pretix.eu (Spanish)
Currently translated at 100.0% (171 of 171 strings)

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

powered by weblate
2021-09-06 20:18:55 +02:00
rauxenz
aa2bd79b99 Translated on translate.pretix.eu (Spanish)
Currently translated at 68.2% (2977 of 4364 strings)

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

powered by weblate
2021-09-06 20:18:55 +02:00
Niklas Forsström
44ee35b885 Translated on translate.pretix.eu (Swedish)
Currently translated at 14.9% (654 of 4364 strings)

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

powered by weblate
2021-09-06 20:18:55 +02:00
Niklas Forsström
22b79a8c22 Translated on translate.pretix.eu (Swedish)
Currently translated at 14.7% (643 of 4364 strings)

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

powered by weblate
2021-09-06 20:18:55 +02:00
Klevagruva
65bbd537e6 Translated on translate.pretix.eu (Swedish)
Currently translated at 14.7% (643 of 4364 strings)

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

powered by weblate
2021-09-06 20:18:55 +02:00
Klevagruva
34387d7bc0 Translated on translate.pretix.eu (Swedish)
Currently translated at 11.1% (486 of 4364 strings)

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

powered by weblate
2021-09-06 20:18:55 +02:00
ityd
ca38204313 Translated on translate.pretix.eu (Spanish)
Currently translated at 68.0% (2969 of 4364 strings)

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

powered by weblate
2021-09-06 20:18:55 +02:00
Niklas Forsström
b7083eca2e Translated on translate.pretix.eu (Swedish)
Currently translated at 10.3% (452 of 4364 strings)

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

powered by weblate
2021-09-06 20:18:55 +02:00
Raphael Michel
6bb8b428dc Stripe: API keys consistently are prefered over connect keys 2021-09-06 20:18:15 +02:00
Raphael Michel
677142d0c9 API: Fix storage of Item.picture 2021-09-06 19:56:57 +02:00
Raphael Michel
d1b66e365a API: Add test case for unsetting settings 2021-09-06 19:33:17 +02:00
Raphael Michel
50154c02ce Voucher: Add error message to form_invalid 2021-09-06 19:32:40 +02:00
Raphael Michel
04375d4fcf Fix voucher form validation (Z#2384192) 2021-09-06 19:32:15 +02:00
Raphael Michel
9c1ff296bb Add missing template 2021-09-06 16:33:41 +02:00
Raphael Michel
0b3acb06b5 Invoice: Fix incorrect reference to original invoice number 2021-09-06 16:33:27 +02:00
Raphael Michel
b2cdccedd6 Docker: Specify distribution of base image, upgrade to Python 3.9 2021-09-05 12:40:21 +02:00
Raphael Michel
7ebefa7b85 Allow to manually bump carts blocking a voucher 2021-08-30 15:57:28 +02:00
Raphael Michel
c7b5baa185 Widget: Only show new tab button on connection error 2021-08-30 15:49:22 +02:00
Raphael Michel
6d08e7a8b0 Docs: libmariadbclient-dev has been replaced by libmariadb-dev 2021-08-30 12:58:15 +02:00
Raphael Michel
0da2b12646 Check-in log exporter: Expose upload time 2021-08-30 12:44:25 +02:00
Raphael Michel
a0693483dc Bump to 4.3.0.dev0 2021-08-27 16:44:36 +02:00
174 changed files with 63805 additions and 27400 deletions

View File

@@ -1,9 +1,9 @@
FROM python:3.8
FROM python:3.9-bullseye
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
default-libmysqlclient-dev \
libmariadb-dev \
gettext \
git \
libffi-dev \
@@ -15,8 +15,7 @@ RUN apt-get update && \
libxslt1-dev \
locales \
nginx \
python-dev \
python-virtualenv \
python3-virtualenv \
python3-dev \
sudo \
supervisor \
@@ -57,6 +56,7 @@ COPY deployment/docker/supervisord /etc/supervisord
COPY deployment/docker/supervisord.all.conf /etc/supervisord.all.conf
COPY deployment/docker/supervisord.web.conf /etc/supervisord.web.conf
COPY deployment/docker/nginx.conf /etc/nginx/nginx.conf
COPY deployment/docker/nginx-max-body-size.conf /etc/nginx/conf.d/nginx-max-body-size.conf
COPY deployment/docker/production_settings.py /pretix/src/production_settings.py
COPY src /pretix/src

View File

@@ -0,0 +1 @@
client_max_body_size 100M;

View File

@@ -16,7 +16,6 @@ http {
charset utf-8;
tcp_nopush on;
tcp_nodelay on;
client_max_body_size 100M;
log_format private '[$time_local] $host "$request" $status $body_bytes_sent';
@@ -66,6 +65,8 @@ http {
access_log off;
expires 365d;
add_header Cache-Control "public";
add_header Access-Control-Allow-Origin "*";
gzip on;
}
location / {
# Very important:

View File

@@ -434,3 +434,19 @@ pretix can make use of some external tools if they are installed. Currently, the
.. _Python documentation: https://docs.python.org/3/library/configparser.html?highlight=configparser#supported-ini-file-structure
.. _Celery documentation: http://docs.celeryproject.org/en/latest/userguide/configuration.html
Maximum upload file sizes
-------------------------
You can configure the maximum file size for uploading various files::
[pretix_file_upload]
; Max upload size for images in MiB, defaults to 10 MiB
max_size_image = 12
; Max upload size for favicons in MiB, defaults to 1 MiB
max_size_favicon = 2
; Max upload size for email attachments in MiB, defaults to 10 MiB
max_size_email_attachment = 15
; Max upload size for other files in MiB, defaults to 10 MiB
; This includes all file upload type order questions
max_size_other = 100

View File

@@ -72,7 +72,7 @@ To build and run pretix, you will need the following debian packages::
# apt-get install git build-essential python-dev python3-venv python3 python3-pip \
python3-dev libxml2-dev libxslt1-dev libffi-dev zlib1g-dev libssl-dev \
gettext libpq-dev libmariadbclient-dev libjpeg-dev libopenjp2-7-dev
gettext libpq-dev libmariadb-dev libjpeg-dev libopenjp2-7-dev
Config file
-----------

View File

@@ -26,6 +26,18 @@ description multi-lingual string A public descri
position integer An integer, used for sorting
require_membership boolean If ``true``, booking this variation requires an active membership.
require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
sales_channels list of strings Sales channels this variation is available on, such as
``"web"`` or ``"resellers"``. Defaults to all existing sales channels.
The item-level list takes precedence, i.e. a sales
channel needs to be on both lists for the item to be
available.
available_from datetime The first date time at which this variation can be bought
(or ``null``).
available_until datetime The last date time at which this variation can be bought
(or ``null``).
hide_without_voucher boolean If ``true``, this variation is only shown during the voucher
redemption process, but not in the normal shop
frontend.
===================================== ========================== =======================================================
Endpoints
@@ -64,6 +76,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": {
"en": "Test2"
},
@@ -129,6 +145,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
}
@@ -160,6 +180,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
}
@@ -181,6 +205,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
}
@@ -233,6 +261,10 @@ Endpoints
"active": false,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}

View File

@@ -107,6 +107,18 @@ variations list of objects A list with one
├ require_membership boolean If ``true``, booking this variation requires an active membership.
├ require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
Markdown syntax or can be ``null``.
├ sales_channels list of strings Sales channels this variation is available on, such as
``"web"`` or ``"resellers"``. Defaults to all existing sales channels.
The item-level list takes precedence, i.e. a sales
channel needs to be on both lists for the item to be
available.
├ available_from datetime The first date time at which this variation can be bought
(or ``null``).
├ available_until datetime The last date time at which this variation can be bought
(or ``null``).
├ hide_without_voucher boolean If ``true``, this variation is only shown during the voucher
redemption process, but not in the normal shop
frontend.
└ position integer An integer, used for sorting
addons list of objects Definition of add-ons that can be chosen for this item.
Only writable during creation,
@@ -230,6 +242,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
},
@@ -241,6 +257,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}
@@ -337,6 +357,10 @@ Endpoints
"require_membership": false,
"require_membership_types": [],
"description": null,
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"position": 0
},
{
@@ -347,6 +371,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}
@@ -422,6 +450,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
},
@@ -433,6 +465,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}
@@ -497,6 +533,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
},
@@ -508,6 +548,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}
@@ -603,6 +647,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
},
@@ -614,6 +662,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}

View File

@@ -4,8 +4,7 @@ Embeddable Widget
=================
If you want to show your ticket shop on your event website or blog, you can use our JavaScript widget. This way,
users will not need to leave your site to buy their ticket in most cases. The widget will still open a new tab
for the checkout if the user is on a mobile device.
users will not need to leave your site to buy their ticket in most cases.
To obtain the correct HTML code for embedding your event into your website, we recommend that you go to the "Widget"
tab of your event's settings. You can specify some optional settings there (for example the language of the widget)

View File

@@ -19,4 +19,4 @@
# 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/>.
#
__version__ = "4.2.0"
__version__ = "4.3.0"

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2.4 on 2021-09-15 11:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixapi', '0006_alter_webhook_target_url'),
]
operations = [
migrations.AlterField(
model_name='webhookcall',
name='target_url',
field=models.URLField(max_length=255),
),
]

View File

@@ -120,7 +120,7 @@ class WebHookEventListener(models.Model):
class WebHookCall(models.Model):
webhook = models.ForeignKey('WebHook', on_delete=models.CASCADE, related_name='calls')
datetime = models.DateTimeField(auto_now_add=True)
target_url = models.URLField()
target_url = models.URLField(max_length=255)
action_type = models.CharField(max_length=255)
is_retry = models.BooleanField(default=False)
execution_time = models.FloatField(null=True)

View File

@@ -54,7 +54,7 @@ from pretix.base.models.items import SubEventItem, SubEventItemVariation
from pretix.base.services.seating import (
SeatProtected, generate_seats, validate_plan_change,
)
from pretix.base.settings import validate_event_settings
from pretix.base.settings import LazyI18nStringList, validate_event_settings
from pretix.base.signals import api_event_settings_fields
logger = logging.getLogger(__name__)
@@ -789,6 +789,10 @@ class EventSettingsSerializer(SettingsSerializer):
data = super().validate(data)
settings_dict = self.instance.freeze()
settings_dict.update(data)
if data.get('confirm_texts') is not None:
data['confirm_texts'] = LazyI18nStringList(data['confirm_texts'])
validate_event_settings(self.event, settings_dict)
return data

View File

@@ -31,9 +31,10 @@
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
import os.path
from decimal import Decimal
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import transaction
from django.db.models import QuerySet
@@ -58,7 +59,8 @@ class InlineItemVariationSerializer(I18nAwareModelSerializer):
model = ItemVariation
fields = ('id', 'value', 'active', 'description',
'position', 'default_price', 'price', 'original_price',
'require_membership', 'require_membership_types',)
'require_membership', 'require_membership_types', 'available_from', 'available_until',
'sales_channels', 'hide_without_voucher',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -73,7 +75,8 @@ class ItemVariationSerializer(I18nAwareModelSerializer):
model = ItemVariation
fields = ('id', 'value', 'active', 'description',
'position', 'default_price', 'price', 'original_price',
'require_membership', 'require_membership_types',)
'require_membership', 'require_membership_types', 'available_from', 'available_until',
'sales_channels', 'hide_without_voucher',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -161,7 +164,7 @@ class ItemSerializer(I18nAwareModelSerializer):
meta_data = MetaDataField(required=False, source='*')
picture = UploadedFileField(required=False, allow_null=True, allowed_types=(
'image/png', 'image/jpeg', 'image/gif'
), max_size=10 * 1024 * 1024)
), max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE)
class Meta:
model = Item
@@ -245,10 +248,13 @@ class ItemSerializer(I18nAwareModelSerializer):
addons_data = validated_data.pop('addons') if 'addons' in validated_data else {}
bundles_data = validated_data.pop('bundles') if 'bundles' in validated_data else {}
meta_data = validated_data.pop('meta_data', None)
picture = validated_data.pop('picture', None)
item = Item.objects.create(**validated_data)
if picture:
item.picture.save(os.path.basename(picture.name), picture)
for variation_data in variations_data:
require_membership_types = variation_data.pop('require_membership_types')
require_membership_types = variation_data.pop('require_membership_types', [])
v = ItemVariation.objects.create(item=item, **variation_data)
if require_membership_types:
v.require_membership_types.add(*require_membership_types)
@@ -269,7 +275,10 @@ class ItemSerializer(I18nAwareModelSerializer):
def update(self, instance, validated_data):
meta_data = validated_data.pop('meta_data', None)
picture = validated_data.pop('picture', None)
item = super().update(instance, validated_data)
if picture:
item.picture.save(os.path.basename(picture.name), picture)
# Meta data
if meta_data is not None:

View File

@@ -26,6 +26,7 @@ from collections import Counter, defaultdict
from decimal import Decimal
import pycountry
from django.conf import settings
from django.core.files import File
from django.db.models import F, Q
from django.utils.timezone import now
@@ -191,7 +192,7 @@ class AnswerSerializer(I18nAwareModelSerializer):
)
if cf.type not in allowed_types:
raise ValidationError('The submitted file "{fid}" has a file type that is not allowed in this field.'.format(fid=data))
if cf.file.size > 10 * 1024 * 1024:
if cf.file.size > settings.FILE_UPLOAD_MAX_SIZE_OTHER:
raise ValidationError('The submitted file "{fid}" is too large to be used in this field.'.format(fid=data))
data['options'] = []

View File

@@ -295,6 +295,7 @@ class OrganizerSettingsSerializer(SettingsSerializer):
'theme_color_background',
'theme_round_borders',
'primary_font',
'organizer_logo_image_inherit',
'organizer_logo_image'
]

View File

@@ -20,6 +20,7 @@
# <https://www.gnu.org/licenses/>.
#
import django_filters
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import transaction
from django.db.models import (
@@ -603,7 +604,7 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
)
if cf.type not in allowed_types:
raise ValidationError('The submitted file "{fid}" has a file type that is not allowed in this field.'.format(fid=data))
if cf.file.size > 10 * 1024 * 1024:
if cf.file.size > settings.FILE_UPLOAD_MAX_SIZE_OTHER:
raise ValidationError('The submitted file "{fid}" is too large to be used in this field.'.format(fid=data))
return cf.file

View File

@@ -82,6 +82,13 @@ class SalesChannel:
"""
return False
@property
def customer_accounts_supported(self) -> bool:
"""
If this property is ``True``, checkout will show the customer login step.
"""
return True
def get_all_sales_channels():
global _ALL_CHANNELS

View File

@@ -462,6 +462,16 @@ def base_placeholders(sender, **kwargs):
lambda waiting_list_entry, event: (waiting_list_entry.subevent or event).get_date_from_display(),
lambda event: (event if not event.has_subevents or not event.subevents.exists() else event.subevents.first()).get_date_from_display()
),
SimpleFunctionalMailTextPlaceholder(
'url_remove', ['waiting_list_entry', 'event'],
lambda waiting_list_entry, event: build_absolute_uri(
event, 'presale:event.waitinglist.remove'
) + '?voucher=' + waiting_list_entry.voucher.code,
lambda event: build_absolute_uri(
event,
'presale:event.waitinglist.remove',
) + '?voucher=68CYU2H6ZTP3WLK5',
),
SimpleFunctionalMailTextPlaceholder(
'url', ['waiting_list_entry', 'event'],
lambda waiting_list_entry, event: build_absolute_uri(

View File

@@ -129,7 +129,7 @@ class OrderListExporter(MultiSheetListExporter):
label=_('End event date'),
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
required=False,
help_text=_('Only include orders including at least one ticket for a date on or after this date. '
help_text=_('Only include orders including at least one ticket for a date on or before this date. '
'Will also include other dates in case of mixed orders!')
)),
]

View File

@@ -46,6 +46,7 @@ import vat_moss.errors
import vat_moss.id
from babel import Locale
from django import forms
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import SimpleUploadedFile
@@ -507,7 +508,7 @@ class PortraitImageField(SizeValidationMixin, ExtValidationMixin, forms.FileFiel
def __init__(self, *args, **kwargs):
kwargs.setdefault('ext_whitelist', (".png", ".jpg", ".jpeg", ".jfif", ".tif", ".tiff", ".bmp"))
kwargs.setdefault('max_size', 10 * 1024 * 1024)
kwargs.setdefault('max_size', settings.FILE_UPLOAD_MAX_SIZE_IMAGE)
super().__init__(*args, **kwargs)
@@ -739,7 +740,7 @@ class BaseQuestionsForm(forms.Form):
".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages",
".bmp", ".tif", ".tiff"
),
max_size=10 * 1024 * 1024,
max_size=settings.FILE_UPLOAD_MAX_SIZE_OTHER,
)
elif q.type == Question.TYPE_DATE:
attrs = {}
@@ -976,7 +977,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
scheme=event.settings.name_scheme,
titles=event.settings.name_scheme_titles,
label=_('Name'),
initial=(self.instance.name_parts if self.instance else self.instance.name_parts),
initial=self.instance.name_parts,
)
if event.settings.invoice_address_required and not event.settings.invoice_address_company_required and not self.all_optional:
if not event.settings.invoice_name_required:

View File

@@ -184,7 +184,7 @@ class BusinessBooleanRadio(forms.RadioSelect):
self.require_business = require_business
if self.require_business:
choices = (
('business', _('Business customer')),
('business', _('Business or institutional customer')),
)
else:
choices = (

View File

@@ -803,7 +803,7 @@ class Modern1Renderer(ClassicInvoiceRenderer):
objects += [
_draw(pgettext('invoice', 'Cancellation number'), self.invoice.number,
value_size, self.left_margin + 50 * mm, 45 * mm),
_draw(pgettext('invoice', 'Original invoice'), self.invoice.number,
_draw(pgettext('invoice', 'Original invoice'), self.invoice.refers.number,
value_size, self.left_margin + 100 * mm, date_x - self.left_margin - 100 * mm - 5 * mm),
]
else:

View File

@@ -0,0 +1,67 @@
#
# 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/>.
#
"""
Django, for theoretically very valid reasons, creates migrations for *every single thing*
we change on a model. Even the `help_text`! This makes sense, as we don't know if any
database backend unknown to us might actually use this information for its database schema.
However, pretix only supports PostgreSQL, MySQL, MariaDB and SQLite and we can be pretty
certain that some changes to models will never require a change to the database. In this case,
not creating a migration for certain changes will save us some performance while applying them
*and* allow for a cleaner git history. Win-win!
Only caveat is that we need to do some dirty monkeypatching to achieve it...
"""
from django.db import models
from django.db.migrations.operations import models as modelops
from django_countries.fields import CountryField
def monkeypatch_migrations():
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("verbose_name")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("verbose_name_plural")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("ordering")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("get_latest_by")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("default_manager_name")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("permissions")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("default_permissions")
IGNORED_ATTRS = [
# (field type, attribute name, banlist of field sub-types)
(models.Field, 'verbose_name', []),
(models.Field, 'help_text', []),
(models.Field, 'validators', []),
(models.Field, 'editable', [models.DateField, models.DateTimeField, models.DateField, models.BinaryField]),
(models.Field, 'blank', [models.DateField, models.DateTimeField, models.AutoField, models.NullBooleanField,
models.TimeField]),
(models.CharField, 'choices', [CountryField])
]
original_deconstruct = models.Field.deconstruct
def new_deconstruct(self):
name, path, args, kwargs = original_deconstruct(self)
for ftype, attr, banlist in IGNORED_ATTRS:
if isinstance(self, ftype) and not any(isinstance(self, ft) for ft in banlist):
kwargs.pop(attr, None)
return name, path, args, kwargs
models.Field.deconstruct = new_deconstruct

View File

@@ -32,53 +32,11 @@
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
"""
Django, for theoretically very valid reasons, creates migrations for *every single thing*
we change on a model. Even the `help_text`! This makes sense, as we don't know if any
database backend unknown to us might actually use this information for its database schema.
However, pretix only supports PostgreSQL, MySQL, MariaDB and SQLite and we can be pretty
certain that some changes to models will never require a change to the database. In this case,
not creating a migration for certain changes will save us some performance while applying them
*and* allow for a cleaner git history. Win-win!
Only caveat is that we need to do some dirty monkeypatching to achieve it...
"""
from django.core.management.commands.makemigrations import Command as Parent
from django.db import models
from django.db.migrations.operations import models as modelops
from django_countries.fields import CountryField
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("verbose_name")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("verbose_name_plural")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("ordering")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("get_latest_by")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("default_manager_name")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("permissions")
modelops.AlterModelOptions.ALTER_OPTION_KEYS.remove("default_permissions")
IGNORED_ATTRS = [
# (field type, attribute name, banlist of field sub-types)
(models.Field, 'verbose_name', []),
(models.Field, 'help_text', []),
(models.Field, 'validators', []),
(models.Field, 'editable', [models.DateField, models.DateTimeField, models.DateField, models.BinaryField]),
(models.Field, 'blank', [models.DateField, models.DateTimeField, models.AutoField, models.NullBooleanField,
models.TimeField]),
(models.CharField, 'choices', [CountryField])
]
from ._migrations import monkeypatch_migrations
original_deconstruct = models.Field.deconstruct
def new_deconstruct(self):
name, path, args, kwargs = original_deconstruct(self)
for ftype, attr, banlist in IGNORED_ATTRS:
if isinstance(self, ftype) and not any(isinstance(self, ft) for ft in banlist):
kwargs.pop(attr, None)
return name, path, args, kwargs
models.Field.deconstruct = new_deconstruct
monkeypatch_migrations()
class Command(Parent):

View File

@@ -32,12 +32,6 @@
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
"""
Django tries to be helpful by suggesting to run "makemigrations" in red font on every "migrate"
run when there are things we have no migrations for. Usually, this is intended, and running
"makemigrations" can really screw up the environment of a user, so we want to prevent novice
users from doing that by going really dirty and filtering it from the output.
"""
import sys
from django.core.management.base import OutputWrapper
@@ -45,9 +39,15 @@ from django.core.management.commands.migrate import Command as Parent
class OutputFilter(OutputWrapper):
"""
Django tries to be helpful by suggesting to run "makemigrations" in red font on every "migrate"
run when there are things we have no migrations for. Usually, this is intended, and running
"makemigrations" can really screw up the environment of a user, so we want to prevent novice
users from doing that by going really dirty and filtering it from the output.
"""
banlist = (
"Your models have changes that are not yet reflected",
"Run 'manage.py makemigrations' to make new "
"have changes that are not yet reflected",
"re-run 'manage.py migrate'"
)
def write(self, msg, style_func=None, ending=None):

View File

@@ -0,0 +1,38 @@
# Generated by Django 3.2.2 on 2021-05-23 13:22
import django.db.models.deletion
from django.db import migrations, models
import pretix.helpers.countries
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0195_auto_20210622_1457'),
]
operations = [
migrations.AddField(
model_name='invoiceaddress',
name='customer',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='invoice_addresses', to='pretixbase.customer'),
),
migrations.CreateModel(
name='AttendeeProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
('attendee_name_cached', models.CharField(max_length=255, null=True)),
('attendee_name_parts', models.JSONField(default=dict)),
('attendee_email', models.EmailField(max_length=254, null=True)),
('company', models.CharField(max_length=255, null=True)),
('street', models.TextField(null=True)),
('zipcode', models.CharField(max_length=30, null=True)),
('city', models.CharField(max_length=255, null=True)),
('country', pretix.helpers.countries.FastCountryField(countries=pretix.helpers.countries.CachedCountries, max_length=2, null=True)),
('state', models.CharField(max_length=255, null=True)),
('answers', models.JSONField(default=list)),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attendee_profiles', to='pretixbase.customer')),
],
),
]

View File

@@ -0,0 +1,36 @@
# Generated by Django 3.2.4 on 2021-09-14 08:14
from django.db import migrations, models
import pretix.base.models.fields
import pretix.base.models.items
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0196_auto_20210523_1322'),
]
operations = [
migrations.AddField(
model_name='itemvariation',
name='available_from',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='itemvariation',
name='available_until',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='itemvariation',
name='hide_without_voucher',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='itemvariation',
name='sales_channels',
field=pretix.base.models.fields.MultiStringField(default=pretix.base.models.items._all_sales_channels_identifiers),
),
]

View File

@@ -19,19 +19,21 @@
# 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 pycountry
from django.conf import settings
from django.contrib.auth.hashers import (
check_password, is_password_usable, make_password,
)
from django.db import models
from django.utils.crypto import get_random_string, salted_hmac
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django_scopes import ScopedManager, scopes_disabled
from pretix.base.banlist import banned
from pretix.base.models.base import LoggedModel
from pretix.base.models.organizer import Organizer
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.helpers.countries import FastCountryField
class Customer(LoggedModel):
@@ -88,6 +90,8 @@ class Customer(LoggedModel):
self.all_logentries().update(data={}, shredded=True)
self.orders.all().update(customer=None)
self.memberships.all().update(attendee_name_parts=None)
self.attendee_profiles.all().delete()
self.invoice_addresses.all().delete()
@scopes_disabled()
def assign_identifier(self):
@@ -174,3 +178,88 @@ class Customer(LoggedModel):
continue
ctx['name_%s' % f] = self.name_parts.get(f, '')
return ctx
@property
def stored_addresses(self):
return self.invoice_addresses(manager='profiles')
class AttendeeProfile(models.Model):
customer = models.ForeignKey(
Customer,
related_name='attendee_profiles',
on_delete=models.CASCADE
)
attendee_name_cached = models.CharField(
max_length=255,
verbose_name=_("Attendee name"),
blank=True, null=True,
)
attendee_name_parts = models.JSONField(
blank=True, default=dict
)
attendee_email = models.EmailField(
verbose_name=_("Attendee email"),
blank=True, null=True,
)
company = models.CharField(max_length=255, blank=True, verbose_name=_('Company name'), null=True)
street = models.TextField(verbose_name=_('Address'), blank=True, null=True)
zipcode = models.CharField(max_length=30, verbose_name=_('ZIP code'), blank=True, null=True)
city = models.CharField(max_length=255, verbose_name=_('City'), blank=True, null=True)
country = FastCountryField(verbose_name=_('Country'), blank=True, blank_label=_('Select country'), null=True)
state = models.CharField(max_length=255, verbose_name=pgettext_lazy('address', 'State'), blank=True, null=True)
answers = models.JSONField(default=list)
objects = ScopedManager(organizer='customer__organizer')
@property
def attendee_name(self):
if not self.attendee_name_parts:
return None
if '_legacy' in self.attendee_name_parts:
return self.attendee_name_parts['_legacy']
if '_scheme' in self.attendee_name_parts:
scheme = PERSON_NAME_SCHEMES[self.attendee_name_parts['_scheme']]
else:
scheme = PERSON_NAME_SCHEMES[self.customer.organizer.settings.name_scheme]
return scheme['concatenation'](self.attendee_name_parts).strip()
@property
def state_name(self):
sd = pycountry.subdivisions.get(code='{}-{}'.format(self.country, self.state))
if sd:
return sd.name
return self.state
@property
def state_for_address(self):
from pretix.base.settings import COUNTRIES_WITH_STATE_IN_ADDRESS
if not self.state or str(self.country) not in COUNTRIES_WITH_STATE_IN_ADDRESS:
return ""
if COUNTRIES_WITH_STATE_IN_ADDRESS[str(self.country)][1] == 'long':
return self.state_name
return self.state
def describe(self):
from .items import Question
from .orders import QuestionAnswer
parts = [
self.attendee_name,
self.attendee_email,
self.company,
self.street,
(self.zipcode or '') + ' ' + (self.city or '') + ' ' + (self.state_for_address or ''),
self.country.name,
]
for a in self.answers:
value = a.get('value')
try:
value = ", ".join(value.values())
except AttributeError:
value = str(value)
answer = QuestionAnswer(question=Question(type=a.get('question_type')), answer=value)
val = str(answer)
parts.append(f'{a["field_label"]}: {val}')
return '\n'.join([str(p).strip() for p in parts if p and str(p).strip()])

View File

@@ -156,6 +156,29 @@ class EventMixin:
return _date(self.date_from.astimezone(tz), "DATE_FORMAT")
return daterange(self.date_from.astimezone(tz), self.date_to.astimezone(tz))
def get_time_range_display(self, tz=None, force_show_end=False) -> str:
"""
Returns a formatted string containing the start time and sometimes the end time
of the event with respect to the current locale and to the ``show_date_to``
setting. Dates are not shown. This is usually used in combination with get_date_range_display
"""
tz = tz or self.timezone
show_date_to = self.date_to and (self.settings.show_date_to or force_show_end) and (
# Show date to if start and end are on the same day ("08:00-10:00")
self.date_to.astimezone(tz).date() == self.date_from.astimezone(tz).date() or
# Show date to if start and end are on consecutive days and less than 24h ("23:00-03:00")
(self.date_to.astimezone(tz).date() == self.date_from.astimezone(tz).date() + timedelta(days=1) and
self.date_to.astimezone(tz).time() < self.date_from.astimezone(tz).time())
# Do not show end time if this is a 5-day event because there's no way to make it understandable
)
if show_date_to:
return '{} {}'.format(
_date(self.date_from.astimezone(tz), "TIME_FORMAT"),
_date(self.date_to.astimezone(tz), "TIME_FORMAT"),
)
return _date(self.date_from.astimezone(tz), "TIME_FORMAT")
@property
def timezone(self):
return pytz.timezone(self.settings.timezone)
@@ -245,6 +268,10 @@ class EventMixin:
).values('items')
sq_active_variation = ItemVariation.objects.filter(
Q(active=True)
& Q(sales_channels__contains=channel)
& Q(hide_without_voucher=False)
& Q(Q(available_from__isnull=True) | Q(available_from__lte=now()))
& Q(Q(available_until__isnull=True) | Q(available_until__gte=now()))
& Q(item__active=True)
& Q(Q(item__available_from__isnull=True) | Q(item__available_from__lte=now()))
& Q(Q(item__available_until__isnull=True) | Q(item__available_until__gte=now()))
@@ -1284,7 +1311,7 @@ class SubEvent(EventMixin, LoggedModel):
verbose_name=_("Frontpage text")
)
seating_plan = models.ForeignKey('SeatingPlan', on_delete=models.PROTECT, null=True, blank=True,
related_name='subevents')
related_name='subevents', verbose_name=_('Seating plan'))
items = models.ManyToManyField('Item', through='SubEventItem')
variations = models.ManyToManyField('ItemVariation', through='SubEventItemVariation')

View File

@@ -736,6 +736,11 @@ class Item(LoggedModel):
return OrderedDict((k, v) for k, v in sorted(data.items(), key=lambda k: k[0]))
def _all_sales_channels_identifiers():
from pretix.base.channels import get_all_sales_channels
return list(get_all_sales_channels().keys())
class ItemVariation(models.Model):
"""
A variation of a product. For example, if your item is 'T-Shirt'
@@ -761,7 +766,7 @@ class ItemVariation(models.Model):
)
value = I18nCharField(
max_length=255,
verbose_name=_('Description')
verbose_name=_('Variation')
)
active = models.BooleanField(
default=True,
@@ -797,6 +802,29 @@ class ItemVariation(models.Model):
verbose_name=_('Membership types'),
blank=True,
)
available_from = models.DateTimeField(
verbose_name=_("Available from"),
null=True, blank=True,
help_text=_('This variation will not be sold before the given date.')
)
available_until = models.DateTimeField(
verbose_name=_("Available until"),
null=True, blank=True,
help_text=_('This variation will not be sold after the given date.')
)
sales_channels = fields.MultiStringField(
verbose_name=_('Sales channels'),
default=_all_sales_channels_identifiers,
help_text=_('The sales channel selection for the product as a whole takes precedence, so if a sales channel is '
'selected here but not on product level, the variation will not be available.'),
blank=True,
)
hide_without_voucher = models.BooleanField(
verbose_name=_('This variation will only be shown if a voucher matching the product is redeemed.'),
default=False,
help_text=_('This variation will be hidden from the event page until the user enters a voucher '
'that unlocks this variation.')
)
objects = ScopedManager(organizer='item__event__organizer')
@@ -928,6 +956,24 @@ class ItemVariation(models.Model):
def is_only_variation(self):
return ItemVariation.objects.filter(item=self.item).count() == 1
def is_available_by_time(self, now_dt: datetime=None) -> bool:
now_dt = now_dt or now()
if self.available_from and self.available_from > now_dt:
return False
if self.available_until and self.available_until < now_dt:
return False
return True
def is_available(self, now_dt: datetime=None) -> bool:
"""
Returns whether this item is available according to its ``active`` flag
and its ``available_from`` and ``available_until`` fields
"""
now_dt = now_dt or now()
if not self.active or not self.is_available_by_time(now_dt):
return False
return True
class ItemAddOn(models.Model):
"""
@@ -1620,6 +1666,8 @@ class Quota(LoggedModel):
@staticmethod
def clean_items(event, items, variations):
if not items:
return
for item in items:
if event != item.event:
raise ValidationError(_('One or more items do not belong to this event.'))

View File

@@ -2334,6 +2334,12 @@ class CartPosition(AbstractPosition):
class InvoiceAddress(models.Model):
last_modified = models.DateTimeField(auto_now=True)
order = models.OneToOneField(Order, null=True, blank=True, related_name='invoice_address', on_delete=models.CASCADE)
customer = models.ForeignKey(
Customer,
related_name='invoice_addresses',
null=True, blank=True,
on_delete=models.CASCADE
)
is_business = models.BooleanField(default=False, verbose_name=_('Business customer'))
company = models.CharField(max_length=255, blank=True, verbose_name=_('Company name'))
name_cached = models.CharField(max_length=255, verbose_name=_('Full name'), blank=True)
@@ -2360,6 +2366,7 @@ class InvoiceAddress(models.Model):
)
objects = ScopedManager(organizer='order__event__organizer')
profiles = ScopedManager(organizer='customer__organizer')
def save(self, **kwargs):
if self.order:
@@ -2372,6 +2379,20 @@ class InvoiceAddress(models.Model):
self.name_parts = {}
super().save(**kwargs)
def describe(self):
parts = [
self.company,
self.name,
self.street,
(self.zipcode or '') + ' ' + (self.city or '') + ' ' + (self.state_for_address or ''),
self.country.name,
self.vat_id,
self.custom_field,
self.internal_reference,
(_('Beneficiary') + ': ' + self.beneficiary) if self.beneficiary else '',
]
return '\n'.join([str(p).strip() for p in parts if p and str(p).strip()])
@property
def is_empty(self):
return (
@@ -2407,6 +2428,30 @@ class InvoiceAddress(models.Model):
raise TypeError("Invalid name given.")
return scheme['concatenation'](self.name_parts).strip()
def for_js(self):
d = {}
if self.name_parts:
if '_scheme' in self.name_parts:
scheme = PERSON_NAME_SCHEMES[self.name_parts['_scheme']]
for i, (k, l, w) in enumerate(scheme['fields']):
d[f'name_parts_{i}'] = self.name_parts.get(k) or ''
d.update({
'company': self.company,
'is_business': self.is_business,
'street': self.street,
'zipcode': self.zipcode,
'city': self.city,
'country': str(self.country) if self.country else None,
'state': str(self.state) if self.state else None,
'vat_id': self.vat_id,
'custom_field': self.custom_field,
'internal_reference': self.internal_reference,
'beneficiary': self.beneficiary,
})
return d
def cachedticket_name(instance, filename: str) -> str:
secret = get_random_string(length=16, allowed_chars=string.ascii_letters + string.digits)

View File

@@ -21,8 +21,9 @@
#
from datetime import timedelta
from django.core.exceptions import ValidationError
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import models, transaction
from django.db.models import F, Q, Sum
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django_scopes import ScopedManager
@@ -114,9 +115,12 @@ class WaitingListEntry(LoggedModel):
return '%s waits for %s' % (str(self.email), str(self.item))
def clean(self):
WaitingListEntry.clean_duplicate(self.email, self.item, self.variation, self.subevent, self.pk)
WaitingListEntry.clean_itemvar(self.event, self.item, self.variation)
WaitingListEntry.clean_subevent(self.event, self.subevent)
try:
WaitingListEntry.clean_duplicate(self.email, self.item, self.variation, self.subevent, self.pk)
WaitingListEntry.clean_itemvar(self.event, self.item, self.variation)
WaitingListEntry.clean_subevent(self.event, self.subevent)
except ObjectDoesNotExist:
raise ValidationError('Invalid input')
def save(self, *args, **kwargs):
update_fields = kwargs.get('update_fields', [])
@@ -147,6 +151,34 @@ class WaitingListEntry(LoggedModel):
)
if availability[1] is None or availability[1] < 1:
raise WaitingListException(_('This product is currently not available.'))
ev = self.subevent or self.event
if ev.seat_category_mappings.filter(product=self.item).exists():
# Generally, we advertise the waiting list to be based on quotas only. This makes it dangerous
# to use in combination with seating plans. If your event has 50 seats and a quota of 50 and
# default settings, everything is fine and the waiting list will work as usual. However, as soon
# as those two numbers diverge, either due to misconfiguration or due to intentional features such
# as our COVID-19 minimum distance feature, things get ugly. Theoretically, there could be
# significant quota available but not a single seat! The waiting list would happily send out vouchers
# which do not work at all. Generally, we consider this a "known bug" and not fixable with the current
# design of the waiting list and seating features.
# However, we've put in a simple safeguard that makes sure the waiting list on its own does not screw
# everything up. Specifically, we will not send out vouchers if the number of available seats is less
# than the number of valid vouchers *issued through the waiting list*. Things can still go wrong due to
# manually created vouchers, manually blocked seats or the minimum distance feature, but this reduces
# the possible damage a bit.
num_free_seats_for_product = ev.free_seats().filter(product=self.item).count()
num_valid_vouchers_for_product = self.event.vouchers.filter(
Q(valid_until__isnull=True) | Q(valid_until__gte=now()),
block_quota=True,
item_id=self.item_id,
subevent_id=self.subevent_id,
waitinglistentries__isnull=False
).aggregate(free=Sum(F('max_usages') - F('redeemed')))['free'] or 0
free_seats = num_free_seats_for_product - num_valid_vouchers_for_product
if not free_seats:
raise WaitingListException(_('No seat with this product is currently available.'))
if self.voucher:
raise WaitingListException(_('A voucher has already been sent to this person.'))
if '@' not in self.email:

View File

@@ -678,9 +678,12 @@ class SeatColumn(ImportColumn):
if value:
try:
value = Seat.objects.get(
event=self.event,
seat_guid=value,
subevent=previous_values.get('subevent')
)
except Seat.MultipleObjectsReturned:
raise ValidationError(_('Multiple matching seats were found.'))
except Seat.DoesNotExist:
raise ValidationError(_('No matching seat was found.'))
if not value.is_available() or value in self._cached:

View File

@@ -283,13 +283,16 @@ class CartManager:
if op.item.require_voucher and op.voucher is None:
raise CartError(error_messages['voucher_required'])
if op.item.hide_without_voucher and (op.voucher is None or not op.voucher.show_hidden_items):
if (
(op.item.hide_without_voucher or (op.variation and op.variation.hide_without_voucher)) and
(op.voucher is None or not op.voucher.show_hidden_items)
):
raise CartError(error_messages['voucher_required'])
if not op.item.is_available() or (op.variation and not op.variation.active):
if not op.item.is_available() or (op.variation and not op.variation.is_available()):
raise CartError(error_messages['unavailable'])
if self._sales_channel not in op.item.sales_channels:
if self._sales_channel not in op.item.sales_channels or (op.variation and self._sales_channel not in op.variation.sales_channels):
raise CartError(error_messages['unavailable'])
if op.subevent and op.item.pk in op.subevent.item_overrides and not op.subevent.item_overrides[op.item.pk].is_available():

View File

@@ -40,7 +40,7 @@ def clean_cart_positions(sender, **kwargs):
cp.delete()
for cp in CartPosition.objects.filter(expires__lt=now() - timedelta(days=14), addon_to__isnull=True):
cp.delete()
for ia in InvoiceAddress.objects.filter(order__isnull=True, last_modified__lt=now() - timedelta(days=14)):
for ia in InvoiceAddress.objects.filter(order__isnull=True, customer__isnull=True, last_modified__lt=now() - timedelta(days=14)):
ia.delete()

View File

@@ -404,7 +404,7 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
logger.exception('Could not attach invoice to email')
pass
if attach_size < 4 * 1024 * 1024:
if attach_size < settings.FILE_UPLOAD_MAX_SIZE_EMAIL_ATTACHMENT:
# Do not attach more than 4MB, it will bounce way to often.
for a in args:
try:

View File

@@ -572,7 +572,7 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
if cp.pk in deleted_positions:
continue
if not cp.item.is_available() or (cp.variation and not cp.variation.active):
if not cp.item.is_available() or (cp.variation and not cp.variation.is_available()):
err = err or error_messages['unavailable']
delete(cp)
continue
@@ -644,7 +644,7 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
err = err or error_messages['voucher_required']
break
if cp.item.hide_without_voucher and (
if (cp.item.hide_without_voucher or (cp.variation and cp.variation.hide_without_voucher)) and (
cp.voucher is None or not cp.voucher.show_hidden_items or not cp.voucher.applies_to(cp.item, cp.variation)
) and not cp.is_bundled:
delete(cp)

View File

@@ -22,12 +22,14 @@
import sys
from datetime import timedelta
from django.db.models import Exists, OuterRef, Q
from django.db.models import Exists, F, OuterRef, Q, Sum
from django.dispatch import receiver
from django.utils.timezone import now
from django_scopes import scopes_disabled
from pretix.base.models import Event, User, WaitingListEntry
from pretix.base.models import (
Event, SeatCategoryMapping, User, WaitingListEntry,
)
from pretix.base.models.waitinglist import WaitingListException
from pretix.base.services.tasks import EventTask
from pretix.base.signals import periodic_task
@@ -43,6 +45,19 @@ def assign_automatically(event: Event, user_id: int=None, subevent_id: int=None)
quota_cache = {}
gone = set()
seats_available = {}
for m in SeatCategoryMapping.objects.filter(event=event).select_related('subevent'):
# See comment in WaitingListEntry.send_voucher() for rationale
num_free_seets_for_product = (m.subevent or event).free_seats().filter(product_id=m.product_id).count()
num_valid_vouchers_for_product = event.vouchers.filter(
Q(valid_until__isnull=True) | Q(valid_until__gte=now()),
block_quota=True,
item_id=m.product_id,
subevent_id=m.subevent_id,
waitinglistentries__isnull=False
).aggregate(free=Sum(F('max_usages') - F('redeemed')))['free'] or 0
seats_available[(m.product_id, m.subevent_id)] = num_free_seets_for_product - num_valid_vouchers_for_product
qs = WaitingListEntry.objects.filter(
event=event, voucher__isnull=True
@@ -70,6 +85,11 @@ def assign_automatically(event: Event, user_id: int=None, subevent_id: int=None)
gone.add((wle.item, wle.variation, wle.subevent))
continue
if (wle.item_id, wle.subevent_id) in seats_available:
if seats_available[wle.item_id, wle.subevent_id] < 1:
gone.add((wle.item, wle.variation, wle.subevent))
continue
quotas = (wle.variation.quotas.filter(subevent=wle.subevent)
if wle.variation
else wle.item.quotas.filter(subevent=wle.subevent))
@@ -91,6 +111,9 @@ def assign_automatically(event: Event, user_id: int=None, subevent_id: int=None)
quota_cache[q.pk][0] if quota_cache[q.pk][0] > 1 else 0,
quota_cache[q.pk][1] - 1 if quota_cache[q.pk][1] is not None else sys.maxsize
)
if (wle.item_id, wle.subevent_id) in seats_available:
seats_available[wle.item_id, wle.subevent_id] -= 1
else:
gone.add((wle.item, wle.variation, wle.subevent))

View File

@@ -1440,6 +1440,7 @@ DEFAULTS = {
('off', _('All refunds are issued to the original payment method')),
('option', _('Customers can choose between a gift card and a refund to their payment method')),
('force', _('All refunds are issued as gift cards')),
('manually', _('Do not handle refunds automatically at all')),
],
),
'form_class': forms.ChoiceField,
@@ -1449,6 +1450,7 @@ DEFAULTS = {
('off', _('All refunds are issued to the original payment method')),
('option', _('Customers can choose between a gift card and a refund to their payment method')),
('force', _('All refunds are issued as gift cards')),
('manually', _('Do not handle refunds automatically at all')),
],
widget=forms.RadioSelect,
# When adding a new ordering, remember to also define it in the event model
@@ -1744,6 +1746,12 @@ Please note that this link is only valid within the next {hours} hours!
We will reassign the ticket to the next person on the list if you do not
redeem the voucher within that timeframe.
If you do NOT need a ticket any more, we kindly ask you to click the
following link to let us know. This way, we can send the ticket as quickly
as possible to the next person on the waiting list:
{url_remove}
Best regards,
Your {event} team"""))
},
@@ -2059,7 +2067,7 @@ Your {organizer} team"""))
'form_kwargs': dict(
label=_('Header image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
max_size=10 * 1024 * 1024,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
help_text=_('If you provide a logo image, we will by default not show your event name and date '
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
'can increase the size with the setting below. We recommend not using small details on the picture '
@@ -2070,7 +2078,7 @@ Your {organizer} team"""))
allowed_types=[
'image/png', 'image/jpeg', 'image/gif'
],
max_size=10 * 1024 * 1024,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
)
},
@@ -2091,7 +2099,8 @@ Your {organizer} team"""))
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_('Show event title even if a header image is present'),
help_text=_('The title will only be shown on the event front page.'),
help_text=_('The title will only be shown on the event front page. If no header image is uploaded for the event, but the header image '
'from the organizer profile is used, this option will be ignored and the event title will always be shown.'),
)
},
'organizer_logo_image': {
@@ -2101,7 +2110,7 @@ Your {organizer} team"""))
'form_kwargs': dict(
label=_('Header image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
max_size=10 * 1024 * 1024,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
help_text=_('If you provide a logo image, we will by default not show your organization name '
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
'can increase the size with the setting below. We recommend not using small details on the picture '
@@ -2112,7 +2121,7 @@ Your {organizer} team"""))
allowed_types=[
'image/png', 'image/jpeg', 'image/gif'
],
max_size=10 * 1024 * 1024,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
)
},
'organizer_logo_image_large': {
@@ -2125,6 +2134,15 @@ Your {organizer} team"""))
help_text=_('We recommend to upload a picture at least 1170 pixels wide.'),
)
},
'organizer_logo_image_inherit': {
'default': 'False',
'type': bool,
'form_class': forms.BooleanField,
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_('Use header image also for events without an individually uploaded logo'),
)
},
'og_image': {
'default': None,
'type': File,
@@ -2132,7 +2150,7 @@ Your {organizer} team"""))
'form_kwargs': dict(
label=_('Social media image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
max_size=10 * 1024 * 1024,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
help_text=_('This picture will be used as a preview if you post links to your ticket shop on social media. '
'Facebook advises to use a picture size of 1200 x 630 pixels, however some platforms like '
'WhatsApp and Reddit only show a square preview, so we recommend to make sure it still looks good '
@@ -2143,7 +2161,7 @@ Your {organizer} team"""))
allowed_types=[
'image/png', 'image/jpeg', 'image/gif'
],
max_size=10 * 1024 * 1024,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
)
},
'invoice_logo_image': {
@@ -2154,7 +2172,7 @@ Your {organizer} team"""))
label=_('Logo image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
required=False,
max_size=10 * 1024 * 1024,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
help_text=_('We will show your logo with a maximal height and width of 2.5 cm.')
),
'serializer_class': UploadedFileField,
@@ -2162,7 +2180,7 @@ Your {organizer} team"""))
allowed_types=[
'image/png', 'image/jpeg', 'image/gif'
],
max_size=10 * 1024 * 1024,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
)
},
'frontpage_text': {

View File

@@ -314,14 +314,14 @@ class AttendeeInfoShredder(BaseDataShredder):
d['data'][i]['attendee_name_parts'] = {
'_legacy': ''
}
if 'company' in row:
d['data'][i]['company'] = ''
if 'street' in row:
d['data'][i]['street'] = ''
if 'zipcode' in row:
d['data'][i]['zipcode'] = ''
if 'city' in row:
d['data'][i]['city'] = ''
if 'company' in row:
d['data'][i]['company'] = ''
if 'street' in row:
d['data'][i]['street'] = ''
if 'zipcode' in row:
d['data'][i]['zipcode'] = ''
if 'city' in row:
d['data'][i]['city'] = ''
le.data = json.dumps(d)
le.shredded = True
le.save(update_fields=['data', 'shredded'])

View File

@@ -24,7 +24,7 @@ import json
from django import template
from django.template.defaultfilters import stringfilter
from pretix.helpers.escapejson import escapejson
from pretix.helpers.escapejson import escapejson, escapejson_attr
register = template.Library()
@@ -40,3 +40,9 @@ def escapejs_filter(value):
def escapejs_dumps_filter(value):
"""Hex encodes characters for use in a application/json type script."""
return escapejson(json.dumps(value))
@register.filter("attr_escapejson_dumps")
def attr_escapejs_dumps_filter(value):
"""Hex encodes characters for use in an HTML attribute."""
return escapejson_attr(json.dumps(value))

View File

@@ -0,0 +1,34 @@
#
# 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 import template
register = template.Library()
@register.filter(name='splitlines')
def splitlines(value):
return value.split("\n")
@register.filter(name='joinlines')
def joinlines(value):
return "\n".join(value)

View File

@@ -135,7 +135,7 @@ def truelink_callback(attrs, new=False):
<a href="https://maps.google.com/location/foo">https://maps.google.com</a>
"""
text = re.sub(r'[^a-zA-Z0-9.\-/_]', '', attrs.get('_text')) # clean up link text
text = re.sub(r'[^a-zA-Z0-9.\-/_ ]', '', attrs.get('_text')) # clean up link text
url = attrs.get((None, 'href'), '/')
href_url = urllib.parse.urlparse(url)
if URL_RE.match(text) and href_url.scheme not in ('tel', 'mailto'):

View File

@@ -27,6 +27,7 @@ from django.urls import reverse
from django.utils.timezone import make_aware
from django.utils.translation import pgettext_lazy
from pretix.base.models import ItemVariation
from pretix.base.reldate import RelativeDateWrapper
from pretix.base.signals import timeline_events
@@ -240,6 +241,39 @@ def timeline_for_event(event, subevent=None):
})
))
for v in ItemVariation.objects.filter(
Q(available_from__isnull=False) | Q(available_until__isnull=False),
item__event=event
).select_related('item'):
if v.available_from:
tl.append(TimelineEvent(
event=event, subevent=subevent,
datetime=v.available_from,
description=pgettext_lazy('timeline', 'Product variation "{product} {variation}" becomes available').format(
product=str(v.item),
variation=str(v.value),
),
edit_url=reverse('control:event.item', kwargs={
'event': event.slug,
'organizer': event.organizer.slug,
'item': v.item.pk,
})
))
if v.available_until:
tl.append(TimelineEvent(
event=event, subevent=subevent,
datetime=v.available_until,
description=pgettext_lazy('timeline', 'Product variation "{product} {variation}" becomes unavailable').format(
product=str(v.item),
variation=str(v.value),
),
edit_url=reverse('control:event.item', kwargs={
'event': event.slug,
'organizer': event.organizer.slug,
'item': v.item.pk,
})
))
pprovs = event.get_payment_providers()
# This is a special case, depending on payment providers not overriding BasePaymentProvider by too much, but it's
# preferrable to having all plugins implement this spearately.

View File

@@ -36,6 +36,7 @@ from pretix.base.models import (
CartPosition, InvoiceAddress, OrderPosition, Question, QuestionAnswer,
QuestionOption,
)
from pretix.base.models.customers import AttendeeProfile
from pretix.presale.signals import contact_form_fields_overrides
@@ -60,6 +61,9 @@ class BaseQuestionsViewMixin:
def get_question_override_sets(self, position):
return []
def question_form_kwargs(self, cr):
return {}
@cached_property
def forms(self):
"""
@@ -71,13 +75,16 @@ class BaseQuestionsViewMixin:
for cr in self._positions_for_questions:
cartpos = cr if isinstance(cr, CartPosition) else None
orderpos = cr if isinstance(cr, OrderPosition) else None
kwargs = self.question_form_kwargs(cr)
form = self.form_class(event=self.request.event,
prefix=cr.id,
cartpos=cartpos,
orderpos=orderpos,
all_optional=self.all_optional,
data=(self.request.POST if self.request.method == 'POST' else None),
files=(self.request.FILES if self.request.method == 'POST' else None))
files=(self.request.FILES if self.request.method == 'POST' else None),
**kwargs)
form.pos = cartpos or orderpos
form.show_copy_answers_to_addon_button = form.pos.addon_to and (
set(form.pos.addon_to.item.questions.all()) & set(form.pos.item.questions.all()) or
@@ -136,25 +143,28 @@ class BaseQuestionsViewMixin:
if not form.is_valid():
failed = True
else:
if form.cleaned_data.get('saved_id'):
prof = AttendeeProfile.objects.filter(
customer=self.cart_customer, pk=form.cleaned_data.get('saved_id')
).first() or AttendeeProfile(customer=getattr(self, 'cart_customer', None))
answers_key_to_index = {a.get('field_name'): i for i, a in enumerate(prof.answers)}
else:
prof = AttendeeProfile(customer=getattr(self, 'cart_customer', None))
answers_key_to_index = {}
# This form was correctly filled, so we store the data as
# answers to the questions / in the CartPosition object
for k, v in form.cleaned_data.items():
if k == 'attendee_name_parts':
if k in ('save', 'saved_id'):
continue
elif k == 'attendee_name_parts':
form.pos.attendee_name_parts = v if v else None
elif k == 'attendee_email':
form.pos.attendee_email = v if v != '' else None
elif k == 'company':
form.pos.company = v if v != '' else None
elif k == 'street':
form.pos.street = v if v != '' else None
elif k == 'zipcode':
form.pos.zipcode = v if v != '' else None
elif k == 'city':
form.pos.city = v if v != '' else None
elif k == 'country':
form.pos.country = v if v != '' else None
elif k == 'state':
form.pos.state = v if v != '' else None
prof.attendee_name_parts = form.pos.attendee_name_parts
prof.attendee_name_cached = form.pos.attendee_name
elif k in ('attendee_email', 'company', 'street', 'zipcode', 'city', 'country', 'state'):
v = v if v != '' else None
setattr(form.pos, k, v)
setattr(prof, k, v)
elif k.startswith('question_'):
field = form.fields[k]
if hasattr(field, 'answer'):
@@ -168,6 +178,23 @@ class BaseQuestionsViewMixin:
else:
self._save_to_answer(field, field.answer, v)
field.answer.save()
if isinstance(field, forms.ModelMultipleChoiceField) or isinstance(field, forms.ModelChoiceField):
answer_value = {o.identifier: str(o) for o in field.answer.options.all()}
elif isinstance(field, forms.BooleanField):
answer_value = bool(field.answer.answer)
else:
answer_value = str(field.answer.answer)
answer_dict = {
'field_name': k,
'field_label': str(field.label),
'value': answer_value,
'question_type': field.question.type,
'question_identifier': field.question.identifier,
}
if k in answers_key_to_index:
prof.answers[answers_key_to_index[k]] = answer_dict
else:
prof.answers.append(answer_dict)
elif v != '' and v is not None:
answer = QuestionAnswer(
cartposition=(form.pos if isinstance(form.pos, CartPosition) else None),
@@ -192,7 +219,27 @@ class BaseQuestionsViewMixin:
self._save_to_answer(field, answer, v)
answer.save()
if isinstance(field, forms.ModelMultipleChoiceField) or isinstance(field, forms.ModelChoiceField):
answer_value = {o.identifier: str(o) for o in answer.options.all()}
elif isinstance(field, forms.BooleanField):
answer_value = bool(answer.answer)
else:
answer_value = str(answer.answer)
answer_dict = {
'field_name': k,
'field_label': str(field.label),
'value': answer_value,
'question_type': field.question.type,
'question_identifier': field.question.identifier,
}
if k in answers_key_to_index:
prof.answers[answers_key_to_index[k]] = answer_dict
else:
prof.answers.append(answer_dict)
else:
field = form.fields[k]
meta_info.setdefault('question_form_data', {})
if v is None:
if k in meta_info['question_form_data']:
@@ -200,8 +247,25 @@ class BaseQuestionsViewMixin:
else:
meta_info['question_form_data'][k] = v
answer_dict = {
'field_name': k,
'field_label': str(field.label),
'value': str(v),
'question_type': None,
'question_identifier': None,
}
if k in answers_key_to_index:
prof.answers[answers_key_to_index[k]] = answer_dict
else:
prof.answers.append(answer_dict)
form.pos.meta_info = json.dumps(meta_info)
form.pos.save()
if form.cleaned_data.get('save') and not failed:
prof.save()
self.cart_session[f'saved_attendee_profile_{form.pos.pk}'] = prof.pk
return not failed
def _save_to_answer(self, field, answer, value):

View File

@@ -683,7 +683,20 @@ class ItemVariationForm(I18nModelForm):
qs = kwargs.pop('membership_types')
super().__init__(*args, **kwargs)
change_decimal_field(self.fields['default_price'], self.event.currency)
self.fields['sales_channels'] = forms.MultipleChoiceField(
label=_('Sales channels'),
required=False,
choices=(
(c.identifier, c.verbose_name) for c in get_all_sales_channels().values()
),
help_text=_('The sales channel selection for the product as a whole takes precedence, so if a sales channel is '
'selected here but not on product level, the variation will not be available.'),
widget=forms.CheckboxSelectMultiple
)
if not self.instance.pk:
self.initial.setdefault('sales_channels', list(get_all_sales_channels().keys()))
self.fields['description'].widget.attrs['rows'] = 3
if qs:
self.fields['require_membership_types'].queryset = qs
else:
@@ -700,9 +713,19 @@ class ItemVariationForm(I18nModelForm):
'original_price',
'description',
'require_membership',
'require_membership_types'
'require_membership_types',
'available_from',
'available_until',
'sales_channels',
'hide_without_voucher',
]
field_classes = {
'available_from': SplitDateTimeField,
'available_until': SplitDateTimeField,
}
widgets = {
'available_from': SplitDateTimePickerWidget(),
'available_until': SplitDateTimePickerWidget(attrs={'data-date-after': '#id_available_from_0'}),
'require_membership_types': forms.CheckboxSelectMultiple(attrs={
'class': 'scrolling-multiple-choice'
}),

View File

@@ -295,6 +295,7 @@ class OrganizerSettingsForm(SettingsForm):
'organizer_homepage_text',
'organizer_link_back',
'organizer_logo_image_large',
'organizer_logo_image_inherit',
'giftcard_length',
'giftcard_expiry_years',
'locales',
@@ -313,7 +314,7 @@ class OrganizerSettingsForm(SettingsForm):
organizer_logo_image = ExtFileField(
label=_('Header image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
max_size=10 * 1024 * 1024,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
required=False,
help_text=_('If you provide a logo image, we will by default not show your organization name '
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
@@ -324,7 +325,7 @@ class OrganizerSettingsForm(SettingsForm):
label=_('Favicon'),
ext_whitelist=(".ico", ".png", ".jpg", ".gif", ".jpeg"),
required=False,
max_size=1 * 1024 * 1024,
max_size=settings.FILE_UPLOAD_MAX_SIZE_FAVICON,
help_text=_('If you provide a favicon, we will show it instead of the default pretix icon. '
'We recommend a size of at least 200x200px to accommodate most devices.')
)

View File

@@ -206,7 +206,7 @@ class VoucherForm(I18nModelForm):
seats_given=data.get('seat') or data.get('seats'),
block_quota=data.get('block_quota')
)
if not self.instance.show_hidden_items and (
if not data.get('show_hidden_items') and (
(self.instance.quota and all(i.hide_without_voucher for i in self.instance.quota.items.all()))
or (self.instance.item and self.instance.item.hide_without_voucher)
):

View File

@@ -43,6 +43,7 @@
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/quota.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/subevent.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/question.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/variations.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/dragndroplist.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/mail.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/orderchange.js" %}"></script>

View File

@@ -256,9 +256,22 @@
<div class="alert alert-info">
{% blocktrans trimmed %}
The waiting list currently is not compatible with some advanced features of pretix such as
seating plans, add-on products or product bundles.
add-on products or product bundles.
{% endblocktrans %}
</div>
<div class="alert alert-info">
{% blocktrans trimmed %}
The waiting list determines availability mainly based on quotas. If you use a seating plan and your
number of available seats is less than the available quota, you might run into situations where
people are sent an email from the waiting list but still are unable to book a seat.
{% endblocktrans %}
<strong>
{% blocktrans trimmed %}
Specifically, this means the waiting list is not safe to use together with the minimum distance
feature of our seating plan module.
{% endblocktrans %}
</strong>
</div>
{% bootstrap_field sform.waiting_list_enabled layout="control" %}
{% bootstrap_field sform.waiting_list_auto layout="control" %}
{% bootstrap_field sform.waiting_list_hours layout="control" %}

View File

@@ -1,37 +1,59 @@
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}" id="item_variations">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div class="panel panel-default" data-formset-form>
<details class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<h4 class="panel-title">
<div class="row">
<div class="col-md-9">
{% bootstrap_field form.value layout='inline' form_group_class="" %}
</div>
<div class="col-md-3 text-right flip">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
{% if form.instance.id %}
<br><small class="text-muted">#{{ form.instance.id }}</small>
{% endif %}
</div>
<summary class="panel-heading">
<div class="row">
<div class="col-md-4 col-xs-12">
<strong class="panel-title">
<span class="fa fa-fw chevron"></span>
<span class="fa fa-warning text-danger hidden variation-error"></span>
<span class="variation-name">
Variation name
</span>
</strong>
<span class="fa fa-warning text-warning hidden variation-warning"></span>
{% if form.instance.id %}
<br>
<small class="text-muted">#{{ form.instance.id }}</small>
{% endif %}
</div>
</h4>
</div>
<div class="col-md-2 col-xs-6">
<span class="fa fa-clock-o fa-fw text-muted variation-timeframe variation-icon-hidden" data-toggle="tooltip" title="{% trans "Only available in a limited timeframe" %}"></span>
<span class="fa fa-tags fa-fw text-muted variation-voucher variation-icon-hidden" data-toggle="tooltip"
title="{% trans "Only visible with a voucher" %}"></span>
<span class="fa fa-id-badge fa-fw text-muted variation-membership variation-icon-hidden" data-toggle="tooltip"
title="{% trans "Require a valid membership" %}"></span>
</div>
<div class="col-md-2 col-xs-6">
{% for k, c in sales_channels.items %}
<span class="fa fa-fw fa-{{ c.icon }} text-muted variation-channel-{{ k }} variation-icon-hidden"
data-toggle="tooltip" title="{% trans c.verbose_name %}"></span>
{% endfor %}
</div>
<div class="col-md-1 col-xs-6 text-right flip variation-price">
<!-- price will be inserted by JS here -->
</div>
<div class="col-md-3 col-xs-6 text-right flip">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</summary>
<div class="panel-body form-horizontal">
{% if form.instance.pk and not form.instance.quotas.exists %}
<div class="alert alert-warning">
@@ -43,9 +65,14 @@
{% endif %}
{% bootstrap_form_errors form %}
{% bootstrap_field form.active layout="control" %}
{% bootstrap_field form.value layout="control" %}
{% bootstrap_field form.default_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.original_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.description layout="control" %}
{% bootstrap_field form.available_from layout="control" %}
{% bootstrap_field form.available_until layout="control" %}
{% bootstrap_field form.sales_channels layout="control" %}
{% bootstrap_field form.hide_without_voucher layout="control" %}
{% if form.require_membership %}
{% bootstrap_field form.require_membership layout="control" %}
<div data-display-dependency="#{{ form.require_membership.id_for_label }}">
@@ -53,39 +80,69 @@
</div>
{% endif %}
</div>
</div>
</details>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="panel panel-default" data-formset-form>
<details class="panel panel-default" data-formset-form open>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<h4 class="panel-title">
<div class="row">
<div class="col-md-9">
{% bootstrap_field formset.empty_form.value layout='inline' form_group_class="" %}
</div>
<div class="col-md-3 text-right flip">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
<summary class="panel-heading">
<div class="row">
<div class="col-md-4 col-xs-12">
<strong class="panel-title">
<span class="fa fa-fw chevron"></span>
<span class="fa fa-warning text-danger hidden variation-error"></span>
<span class="variation-name">
{% trans "New variation" %}
</span>
</strong>
<span class="fa fa-warning text-warning hidden variation-warning"></span>
{% if form.instance.id %}
<br>
<small class="text-muted">#{{ form.instance.id }}</small>
{% endif %}
</div>
</h4>
</div>
<div class="col-md-2 col-xs-6">
<span class="fa fa-clock-o fa-fw text-muted variation-timeframe variation-icon-hidden" data-toggle="tooltip" title="{% trans "Only available in a limited timeframe" %}"></span>
<span class="fa fa-tags fa-fw text-muted variation-voucher variation-icon-hidden" data-toggle="tooltip"
title="{% trans "Only visible with a voucher" %}"></span>
<span class="fa fa-id-badge fa-fw text-muted variation-membership variation-icon-hidden" data-toggle="tooltip"
title="{% trans "Require a valid membership" %}"></span>
</div>
<div class="col-md-2 col-xs-6">
{% for k, c in sales_channels.items %}
<span class="fa fa-fw fa-{{ c.icon }} text-muted variation-channel-{{ k }} variation-icon-hidden"
data-toggle="tooltip" title="{% trans c.verbose_name %}"></span>
{% endfor %}
</div>
<div class="col-md-1 col-xs-6 text-right flip variation-price">
<!-- price will be inserted by JS here -->
</div>
<div class="col-md-3 col-xs-6 text-right flip">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</summary>
<div class="panel-body form-horizontal">
{% bootstrap_field formset.empty_form.active layout="control" %}
{% bootstrap_field formset.empty_form.value layout="control" %}
{% bootstrap_field formset.empty_form.default_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field formset.empty_form.original_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field formset.empty_form.description layout="control" %}
{% bootstrap_field formset.empty_form.available_from layout="control" %}
{% bootstrap_field formset.empty_form.available_until layout="control" %}
{% bootstrap_field formset.empty_form.sales_channels layout="control" %}
{% bootstrap_field formset.empty_form.hide_without_voucher layout="control" %}
{% if formset.empty_form.require_membership %}
{% bootstrap_field formset.empty_form.require_membership layout="control" %}
<div data-display-dependency="#{{ formset.empty_form.require_membership.id_for_label }}">
@@ -93,7 +150,7 @@
</div>
{% endif %}
</div>
</div>
</details>
{% endescapescript %}
</script>
<p>

View File

@@ -111,7 +111,7 @@
{% endif %}
</td>
<td>
{{ m.attendee_name }}
{{ m.attendee_name|default_if_none:"" }}
</td>
<td>
<div class="quotabox">

View File

@@ -41,6 +41,7 @@
<legend>{% trans "Organizer page" %}</legend>
{% bootstrap_field sform.organizer_logo_image layout="control" %}
{% bootstrap_field sform.organizer_logo_image_large layout="control" %}
{% bootstrap_field sform.organizer_logo_image_inherit layout="control" %}
{% bootstrap_field sform.organizer_homepage_text layout="control" %}
{% bootstrap_field sform.event_list_type layout="control" %}
{% bootstrap_field sform.event_list_availability layout="control" %}

View File

@@ -0,0 +1,20 @@
{% extends "pretixcontrol/items/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Delete carts" %}{% endblock %}
{% block inside %}
<h1>{% trans "Delete carts" %}</h1>
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<p>{% blocktrans %}Are you sure you want to delete any cart positions with voucher <strong>{{ voucher }}</strong>?{% endblocktrans %}</p>
<p>{% blocktrans %}This will silently remove products from the cart of a user currently making a purchase. This can be really confusing. Only use this if you know that the session is no longer in use.{% endblocktrans %}</p>
<div class="form-group submit-group">
<a href="{% url "control:event.vouchers" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default btn-cancel">
{% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-danger btn-save">
{% trans "Delete" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -10,14 +10,26 @@
<div class="alert alert-warning">
{% trans "This voucher already has been used. It is not recommended to modify it." %}
<ul>
{% for order in voucher.distinct_orders %}
<li><a href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
{% blocktrans with code=order.code %}Order {{ code }}{% endblocktrans %}
</a></li>
{% endfor %}
{% for order in voucher.distinct_orders %}
<li><a href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
{% blocktrans with code=order.code %}Order {{ code }}{% endblocktrans %}
</a></li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if redeemed_in_carts %}
<div class="alert alert-warning">
{% blocktrans trimmed with number=redeemed_in_carts %}
This voucher is currently used in {{ number }} cart sessions and there might not be free to use until the cart sessions
expire.
{% endblocktrans %}
<p class="text-right">
<a href="{% url "control:event.voucher.deletecarts" organizer=request.event.organizer.slug event=request.event.slug voucher=voucher.id %}" class="btn btn-default"><i class="fa fa-trash"></i> {% trans "Remove cart positions" %}</a>
</p>
<div class="clear"></div>
</div>
{% endif %}
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
{% bootstrap_form_errors form %}

View File

@@ -276,6 +276,8 @@ urlpatterns = [
re_path(r'^vouchers/(?P<voucher>\d+)/$', vouchers.VoucherUpdate.as_view(), name='event.voucher'),
re_path(r'^vouchers/(?P<voucher>\d+)/delete$', vouchers.VoucherDelete.as_view(),
name='event.voucher.delete'),
re_path(r'^vouchers/(?P<voucher>\d+)/deletecarts$', vouchers.VoucherDeleteCarts.as_view(),
name='event.voucher.deletecarts'),
re_path(r'^vouchers/add$', vouchers.VoucherCreate.as_view(), name='event.vouchers.add'),
re_path(r'^vouchers/go$', vouchers.VoucherGo.as_view(), name='event.vouchers.go'),
re_path(r'^vouchers/bulk_add$', vouchers.VoucherBulkCreate.as_view(), name='event.vouchers.bulk'),

View File

@@ -1331,6 +1331,7 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataE
"Your participants won't be able to buy the bundle unless you remove this "
"item from it."))
ctx['sales_channels'] = get_all_sales_channels()
return ctx
@cached_property

View File

@@ -69,7 +69,7 @@ class ImportView(EventPermissionRequiredMixin, TemplateView):
'event': request.event.slug,
'organizer': request.organizer.slug,
}))
if request.FILES['file'].size > 1024 * 1024 * 10:
if request.FILES['file'].size > settings.FILE_UPLOAD_MAX_SIZE_OTHER:
messages.error(request, _('Please do not upload files larger than 10 MB.'))
return redirect(reverse('control:event.orders.import', kwargs={
'event': request.event.slug,

View File

@@ -58,7 +58,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
accepted_formats = (
'application/pdf',
)
maxfilesize = 1024 * 1024 * 10
maxfilesize = settings.FILE_UPLOAD_MAX_SIZE_IMAGE
minfilesize = 10
title = None

View File

@@ -50,6 +50,7 @@ from django.urls import resolve, reverse
from django.utils.functional import cached_property
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from django.views.generic import (
CreateView, DeleteView, ListView, TemplateView, UpdateView, View,
@@ -181,6 +182,38 @@ class VoucherTags(EventPermissionRequiredMixin, TemplateView):
return VoucherTagFilterForm(data=self.request.GET, event=self.request.event)
class VoucherDeleteCarts(EventPermissionRequiredMixin, DeleteView):
model = Voucher
template_name = 'pretixcontrol/vouchers/delete_carts.html'
permission = 'can_change_vouchers'
context_object_name = 'voucher'
def get_object(self, queryset=None) -> Voucher:
try:
return self.request.event.vouchers.get(
id=self.kwargs['voucher']
)
except Voucher.DoesNotExist:
raise Http404(_("The requested voucher does not exist."))
@transaction.atomic
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
success_url = self.get_success_url()
self.object.log_action('pretix.voucher.carts.deleted', user=self.request.user)
CartPosition.objects.filter(addon_to__voucher=self.object).delete()
self.object.cartposition_set.all().delete()
messages.success(request, _('The selected cart positions have been removed.'))
return HttpResponseRedirect(success_url)
def get_success_url(self) -> str:
return reverse('control:event.vouchers', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
})
class VoucherDelete(EventPermissionRequiredMixin, DeleteView):
model = Voucher
template_name = 'pretixcontrol/vouchers/delete.html'
@@ -229,6 +262,10 @@ class VoucherUpdate(EventPermissionRequiredMixin, UpdateView):
permission = 'can_change_vouchers'
context_object_name = 'voucher'
def form_invalid(self, form):
messages.error(self.request, _('We could not save your changes. See below for details.'))
return super().form_invalid(form)
def get_form_class(self):
form_class = VoucherForm
for receiver, response in voucher_form_class.send(self.request.event, cls=form_class):
@@ -262,6 +299,15 @@ class VoucherUpdate(EventPermissionRequiredMixin, UpdateView):
'event': self.request.event.slug,
})
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
redeemed_in_carts = CartPosition.objects.filter(
voucher=self.object, event=self.request.event,
expires__gte=now()
).count()
ctx['redeemed_in_carts'] = redeemed_in_carts
return ctx
class VoucherCreate(EventPermissionRequiredMixin, CreateView):
model = Voucher
@@ -269,6 +315,10 @@ class VoucherCreate(EventPermissionRequiredMixin, CreateView):
permission = 'can_change_vouchers'
context_object_name = 'voucher'
def form_invalid(self, form):
messages.error(self.request, _('We could not save your changes. See below for details.'))
return super().form_invalid(form)
def get_form_class(self):
form_class = VoucherForm
for receiver, response in voucher_form_class.send(self.request.event, cls=form_class):
@@ -451,6 +501,10 @@ class VoucherBulkCreate(EventPermissionRequiredMixin, AsyncFormView):
ctx['code_length'] = settings.ENTROPY['voucher_code']
return ctx
def form_invalid(self, form):
messages.error(self.request, _('We could not save your changes. See below for details.'))
return super().form_invalid(form)
class VoucherRNG(EventPermissionRequiredMixin, View):
permission = 'can_change_vouchers'

View File

@@ -50,7 +50,7 @@ from django.views import View
from django.views.generic import ListView
from django.views.generic.edit import DeleteView
from pretix.base.models import Item, WaitingListEntry
from pretix.base.models import Item, Quota, WaitingListEntry
from pretix.base.models.waitinglist import WaitingListException
from pretix.base.services.waitinglist import assign_automatically
from pretix.base.views.tasks import AsyncAction
@@ -239,8 +239,24 @@ class WaitingListView(EventPermissionRequiredMixin, WaitingListQuerySetMixin, Pa
if wle.variation
else wle.item.check_quotas(count_waitinglist=False, subevent=wle.subevent, _cache=quota_cache)
)
if wle.availability[0] == Quota.AVAILABILITY_OK and ev.seat_category_mappings.filter(product=wle.item).exists():
# See comment in WaitingListEntry.send_voucher() for rationale
num_free_seats_for_product = ev.free_seats().filter(product=wle.item).count()
num_valid_vouchers_for_product = self.request.event.vouchers.filter(
Q(valid_until__isnull=True) | Q(valid_until__gte=now()),
block_quota=True,
item_id=wle.item_id,
subevent=wle.subevent_id,
waitinglistentries__isnull=False
).aggregate(free=Sum(F('max_usages') - F('redeemed')))['free'] or 0
free_seats = num_free_seats_for_product - num_valid_vouchers_for_product
wle.availability = (
Quota.AVAILABILITY_GONE if free_seats == 0 else wle.availability[0],
min(free_seats, wle.availability[1])
)
itemvar_cache[(wle.item, wle.variation, wle.subevent)] = wle.availability
if wle.availability[0] == 100:
if wle.availability[0] == Quota.AVAILABILITY_OK:
any_avail = True
ctx['any_avail'] = any_avail

View File

@@ -28,10 +28,14 @@ from pretix.base.reldate import RelativeDateWrapper
class CustomJSONEncoder(I18nJSONEncoder):
def default(self, obj):
from pretix.base.settings import LazyI18nStringList
if isinstance(obj, RelativeDateWrapper):
return obj.to_string()
elif isinstance(obj, File):
return obj.name
elif isinstance(obj, LazyI18nStringList):
return [s.data for s in obj.data]
if isinstance(obj, PhoneNumber):
return str(obj)
else:

File diff suppressed because it is too large Load Diff

View File

@@ -7,9 +7,9 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"PO-Revision-Date: 2021-06-14 18:45+0000\n"
"Last-Translator: Abdullah <abdullah.gumaijan@gmail.com>\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2021-09-15 11:22+0000\n"
"Last-Translator: Mohamed Tawfiq <mtawfiq@wafyapp.com>\n"
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
"ar/>\n"
"Language: ar\n"
@@ -18,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
"X-Generator: Weblate 4.6\n"
"X-Generator: Weblate 4.8\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -197,7 +197,7 @@ msgstr "تذاكر سارية المفعول"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
msgid "Currently inside"
msgstr "في الداخل حاليا"
msgstr "حاليا بالداخل"
#: pretix/static/pretixbase/js/asynctask.js:43
#: pretix/static/pretixbase/js/asynctask.js:119
@@ -471,10 +471,12 @@ msgid "Count"
msgstr "احسب"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "نعم"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "لا"
@@ -518,20 +520,20 @@ msgstr "ستسترد %(currency)%(amount)"
msgid "Please enter the amount the organizer can keep."
msgstr "الرجاء إدخال المبلغ الذي يمكن للمنظم الاحتفاظ به."
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr "الرجاء إدخال عدد لأحد أنواع التذاكر."
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
msgid "required"
msgstr "مطلوب"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr "المنطقة الزمنية:"
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr "التوقيت المحلي:"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2020-12-19 07:00+0000\n"
"Last-Translator: albert <albert.serra.monner@gmail.com>\n"
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -459,10 +459,12 @@ msgid "Count"
msgstr "Quantitat"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr ""
@@ -498,22 +500,22 @@ msgstr ""
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "Cistella expirada"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2021-08-11 05:00+0000\n"
"Last-Translator: Michael <michael.happl@gmx.at>\n"
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -474,10 +474,12 @@ msgid "Count"
msgstr "Počet"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Ano"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "Ne"
@@ -518,20 +520,20 @@ msgstr "Dostanete %(currency)s %(amount)s zpět"
msgid "Please enter the amount the organizer can keep."
msgstr "Zadejte částku, kterou si organizátor může ponechat."
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr "Zadejte prosím množství pro jeden z typů vstupenek."
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
msgid "required"
msgstr "povinný"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr "Časové pásmo:"
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr "Místní čas:"

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"PO-Revision-Date: 2020-09-15 02:00+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2021-09-13 09:48+0000\n"
"Last-Translator: Mie Frydensbjerg <mif@aarhus.dk>\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 3.10.3\n"
"X-Generator: Weblate 4.8\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -501,14 +501,14 @@ msgid "Count"
msgstr "Antal"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Ja"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#, fuzzy
#| msgid "None"
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "Ingen"
msgstr "Nej"
#: pretix/static/pretixcontrol/js/ui/subevent.js:111
msgid "(one more date)"
@@ -548,22 +548,22 @@ msgstr "fra %(currency)s %(price)s"
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "Kurv udløbet"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr "Tidszone:"
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr "Din lokaltid:"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2021-08-18 01:00+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -480,10 +480,12 @@ msgid "Count"
msgstr "Anzahl"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Ja"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "Nein"
@@ -521,20 +523,20 @@ msgstr "Sie erhalten %(currency)s %(amount)s zurück"
msgid "Please enter the amount the organizer can keep."
msgstr "Bitte geben Sie den Betrag ein, den der Veranstalter einbehalten darf."
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr "Bitte tragen Sie eine Menge für eines der Produkte ein."
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
msgid "required"
msgstr "verpflichtend"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr "Zeitzone:"
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr "Deine lokale Zeit:"

View File

@@ -216,6 +216,7 @@ Security
SEPA
Shirts
Signaturverfahren
Sitzplanmoduls
Social
Sofort
SOFORT
@@ -276,6 +277,7 @@ Veranstalterkonten
Veranstalterkonto
Veranstalterkontos
Veranstaltername
Veranstalterprofil
Veranstalterseite
Veranstalterübersicht
Veranstaltungs

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2021-08-18 01:00+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
@@ -479,10 +479,12 @@ msgid "Count"
msgstr "Anzahl"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Ja"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "Nein"
@@ -520,20 +522,20 @@ msgstr "Du erhältst %(currency)s %(amount)s zurück"
msgid "Please enter the amount the organizer can keep."
msgstr "Bitte gib den Betrag ein, den der Veranstalter einbehalten darf."
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr "Bitte trage eine Menge für eines der Produkte ein."
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
msgid "required"
msgstr "verpflichtend"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr "Zeitzone:"
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr "Deine lokale Zeit:"

View File

@@ -216,6 +216,7 @@ Security
SEPA
Shirts
Signaturverfahren
Sitzplanmoduls
Social
Sofort
SOFORT
@@ -276,6 +277,7 @@ Veranstalterkonten
Veranstalterkonto
Veranstalterkontos
Veranstaltername
Veranstalterprofil
Veranstalterseite
Veranstalterübersicht
Veranstaltungs

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -458,10 +458,12 @@ msgid "Count"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr ""
@@ -497,20 +499,20 @@ msgstr ""
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
msgid "required"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2019-10-03 19:00+0000\n"
"Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n"
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -513,10 +513,12 @@ msgid "Count"
msgstr "Λογαριασμός"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Ναι"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "Όχι"
@@ -558,22 +560,22 @@ msgstr "απο %(currency)s %(price)s"
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr "Εισαγάγετε μια ποσότητα για έναν από τους τύπους εισιτηρίων."
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "Το καλάθι έληξε"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,9 +7,9 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"PO-Revision-Date: 2020-04-27 20:00+0000\n"
"Last-Translator: Gonzalo Gabriel Perez <zalitoar@gmail.com>\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2021-09-06 09:00+0000\n"
"Last-Translator: rauxenz <noquedandireccionesdisponibles@gmail.com>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
"js/es/>\n"
"Language: 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 3.10.3\n"
"X-Generator: Weblate 4.6\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -62,174 +62,151 @@ msgstr "Contactando con el banco…"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:30
msgid "Select a check-in list"
msgstr ""
msgstr "Seleccione una lista de registro"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:31
msgid "No active check-in lists found."
msgstr ""
msgstr "No se encontraron listas de registro activas."
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:32
msgid "Switch check-in list"
msgstr ""
msgstr "Cambiar lista de registro"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:33
msgid "Search results"
msgstr ""
msgstr "Resultados de búsqueda"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:34
#, fuzzy
#| msgctxt "widget"
#| msgid "Close ticket shop"
msgid "No tickets found"
msgstr "Cerrar tienda de tickets"
msgstr "No se encontraron tickets"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:35
msgid "Result"
msgstr ""
msgstr "Resultado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:36
msgid "This ticket requires special attention"
msgstr ""
msgstr "Este ticket requiere atención especial"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:37
msgid "Switch direction"
msgstr ""
msgstr "Cambiar dirección"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:38
msgid "Entry"
msgstr ""
msgstr "Ingreso"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:39
msgid "Exit"
msgstr ""
msgstr "Salida"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:40
msgid "Scan a ticket or search and press return…"
msgstr ""
msgstr "Escanee un ticket o busque y presione regresar…"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:41
msgid "Load more"
msgstr ""
msgstr "Cargar más"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:42
msgid "Valid"
msgstr ""
msgstr "Válido"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:43
msgid "Unpaid"
msgstr ""
msgstr "Por pagar"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:44
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:45
msgid "Canceled"
msgstr ""
msgstr "Cancelado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:46
#, fuzzy
#| msgctxt "widget"
#| msgid "Redeem"
msgid "Redeemed"
msgstr "Utilizar cupón"
msgstr "Canjeado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:47
msgid "Cancel"
msgstr ""
msgstr "Cancelar"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
#, fuzzy
#| msgctxt "widget"
#| msgid "Continue"
msgid "Continue"
msgstr "Continuar"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:49
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58
msgid "Ticket not paid"
msgstr ""
msgstr "Ticket por pagar"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:50
msgid "This ticket is not yet paid. Do you want to continue anyways?"
msgstr ""
msgstr "Este ticket aún no ha sido pagado ¿Desea continuar de todos modos?"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
msgid "Additional information required"
msgstr ""
msgstr "Se requiere información adicional"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
msgid "Valid ticket"
msgstr ""
msgstr "Ticket válido"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:53
msgid "Exit recorded"
msgstr ""
msgstr "Salida registrada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:54
msgid "Ticket already used"
msgstr ""
msgstr "Este ticket ya fue utilizado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:55
msgid "Information required"
msgstr ""
msgstr "Información requerida"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56
#, fuzzy
#| msgid "Unknown error."
msgid "Unknown ticket"
msgstr "Error desconocido."
msgstr "Ticket no encontrado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57
msgid "Ticket type not allowed here"
msgstr ""
msgstr "Tipo de ticket no está permitido"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:59
msgid "Entry not allowed"
msgstr ""
msgstr "Entrada no permitida"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
msgid "Ticket code revoked/changed"
msgstr ""
msgstr "Código de ticket revocado/cambiado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
msgid "Order canceled"
msgstr ""
msgstr "Orden cancelada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
#, fuzzy
#| msgid "Check-in QR"
msgid "Checked-in Tickets"
msgstr "QR de Chequeo"
msgstr "Registro de código QR"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
msgid "Valid Tickets"
msgstr ""
msgstr "Tickets válidos"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
msgid "Currently inside"
msgstr ""
msgstr "Actualmente adentro"
#: pretix/static/pretixbase/js/asynctask.js:43
#: pretix/static/pretixbase/js/asynctask.js:119
#, fuzzy
#| msgid ""
#| "Your request has been queued on the server and will now be processed. "
#| "Depending on the size of your event, this might take up to a few minutes."
msgid ""
"Your request is currently being processed. Depending on the size of your "
"event, this might take up to a few minutes."
msgstr ""
"Su solicitud ha sido enviada al servidor y será procesada en breve. Esto "
"puede tomar uno o varios minutos, en dependencia del tamaño de su evento."
"Su solicitud está siendo procesada. Esto puede tardar varios minutos, "
"dependiendo del tamaño de su evento."
#: pretix/static/pretixbase/js/asynctask.js:48
#: pretix/static/pretixbase/js/asynctask.js:124
#, fuzzy
#| msgid ""
#| "Your request has been queued on the server and will now be processed. "
#| "Depending on the size of your event, this might take up to a few minutes."
msgid "Your request has been queued on the server and will soon be processed."
msgstr ""
"Su solicitud ha sido enviada al servidor y será procesada en breve. Esto "
"puede tomar uno o varios minutos, en dependencia del tamaño de su evento."
msgstr "Su solicitud ha sido enviada al servidor y será procesada en breve."
#: pretix/static/pretixbase/js/asynctask.js:54
#: pretix/static/pretixbase/js/asynctask.js:130
@@ -240,7 +217,7 @@ msgid ""
msgstr ""
"Su solicitud llegó al servidor pero seguimos esperando a que sea procesada. "
"Si toma más de dos minutos, por favor contáctenos o regrese a la página "
"anterior en su navegador y pruebe de nuevo."
"anterior en su navegador e intente de nuevo."
#: pretix/static/pretixbase/js/asynctask.js:89
#: pretix/static/pretixbase/js/asynctask.js:175
@@ -259,17 +236,15 @@ msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:144
#: pretix/static/pretixcontrol/js/ui/mail.js:21
#, fuzzy
#| msgid "The request took to long. Please try again."
msgid "The request took too long. Please try again."
msgstr "La solicitud ha tomado demasiado tiempo. Por favor, pruebe de nuevo."
msgstr "La solicitud ha tomado demasiado tiempo. Por favor, intente de nuevo."
#: pretix/static/pretixbase/js/asynctask.js:183
#: pretix/static/pretixcontrol/js/ui/mail.js:26
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
msgstr ""
"Ahora mismo no podemos contactar con el servidor. Por favor, pruebe de "
"Ahora mismo no podemos contactar con el servidor. Por favor, intente de "
"nuevo. Código de error: {code}"
#: pretix/static/pretixbase/js/asynctask.js:205
@@ -283,8 +258,8 @@ msgid ""
"page and try again."
msgstr ""
"Estamos enviando su solicitud al servidor. Si este proceso toma más de un "
"minuto, por favor, revise su conexión a Internet, recargue la página y "
"pruebe de nuevo."
"minuto, por favor, revise su conexión a Internet, recargue la página e "
"intente nuevamente."
#: pretix/static/pretixbase/js/asynctask.js:262
#: pretix/static/pretixcontrol/js/ui/main.js:34
@@ -302,82 +277,79 @@ msgstr "¡Presione Control+C para copiar!"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:10
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:16
msgid "is one of"
msgstr ""
msgstr "es uno de"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:22
msgid "is before"
msgstr ""
msgstr "está antes"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:26
msgid "is after"
msgstr ""
msgstr "está después"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:59
msgid "Product"
msgstr ""
msgstr "Producto"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:63
#, fuzzy
#| msgctxt "widget"
#| msgid "See variations"
msgid "Product variation"
msgstr "Ver variaciones"
msgstr "Ver variaciones del producto"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:67
msgid "Current date and time"
msgstr ""
msgstr "Fecha y hora"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:71
msgid "Number of previous entries"
msgstr ""
msgstr "Número de entradas previas"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:75
msgid "Number of previous entries since midnight"
msgstr ""
msgstr "Número de entradas previas desde medianoche"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:79
msgid "Number of days with a previous entry"
msgstr ""
msgstr "Número de días con una entrada previa"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:97
msgid "All of the conditions below (AND)"
msgstr ""
msgstr "Todas las siguientes condiciones (Y)"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:98
msgid "At least one of the conditions below (OR)"
msgstr ""
msgstr "Al menos una de las siguientes condiciones (O)"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:99
msgid "Event start"
msgstr ""
msgstr "Inicio del evento"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:100
msgid "Event end"
msgstr ""
msgstr "Fin del evento"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:101
msgid "Event admission"
msgstr ""
msgstr "Admisión al evento"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:102
msgid "custom date and time"
msgstr ""
msgstr "seleccionar fecha y hora"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:103
msgid "custom time"
msgstr ""
msgstr "seleccionar hora"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:104
msgid "Tolerance (minutes)"
msgstr ""
msgstr "Tolerancia (en minutos)"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:105
msgid "Add condition"
msgstr ""
msgstr "Añadir condición"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:106
msgid "minutes"
msgstr ""
msgstr "minutos"
#: pretix/static/pretixcontrol/js/ui/editor.js:69
msgid "Lead Scan QR"
@@ -390,7 +362,7 @@ msgstr "QR de Chequeo"
#: pretix/static/pretixcontrol/js/ui/editor.js:313
msgid "The PDF background file could not be loaded for the following reason:"
msgstr ""
"El fondo del archivo PDF no ha podido ser cargado por la siguiente razón:"
"El archivo PDF de fondo no ha podido ser cargado debido al siguiente motivo:"
#: pretix/static/pretixcontrol/js/ui/editor.js:521
msgid "Group of objects"
@@ -405,10 +377,8 @@ msgid "Barcode area"
msgstr "Área para código de barras"
#: pretix/static/pretixcontrol/js/ui/editor.js:531
#, fuzzy
#| msgid "Barcode area"
msgid "Image area"
msgstr "Área para código de barras"
msgstr "Área de imagen"
#: pretix/static/pretixcontrol/js/ui/editor.js:533
msgid "Powered by pretix"
@@ -451,12 +421,12 @@ msgstr "Error desconocido."
#: pretix/static/pretixcontrol/js/ui/main.js:271
msgid "Your color has great contrast and is very easy to read!"
msgstr "¡Tu color tiene gran contraste y es muy legible!"
msgstr "¡Tu color tiene gran contraste y es muy fácil de leer!"
#: pretix/static/pretixcontrol/js/ui/main.js:275
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
"¡Tu color tiene un contraste decente y es probablemente suficientemente "
"¡Tu color tiene un contraste decente y es probablemente lo suficientemente "
"legible!"
#: pretix/static/pretixcontrol/js/ui/main.js:279
@@ -464,7 +434,7 @@ msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
msgstr ""
"Tu color tiene mal contraste para un texto con fondo blanco, por favor "
"Tu color tiene mal contraste para un texto con fondo blanco, por favor, "
"escoge un tono más oscuro."
#: pretix/static/pretixcontrol/js/ui/main.js:417
@@ -477,11 +447,11 @@ msgstr "Ninguno"
#: pretix/static/pretixcontrol/js/ui/main.js:419
msgid "Search query"
msgstr ""
msgstr "Consultar búsqueda"
#: pretix/static/pretixcontrol/js/ui/main.js:422
msgid "Selected only"
msgstr ""
msgstr "Solamente seleccionados"
#: pretix/static/pretixcontrol/js/ui/main.js:834
msgid "Use a different name internally"
@@ -508,10 +478,12 @@ msgid "Count"
msgstr "Cantidad"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Si"
msgstr "Sí"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "No"
@@ -519,11 +491,11 @@ msgstr "No"
msgid "(one more date)"
msgid_plural "({num} more dates)"
msgstr[0] "(una fecha más)"
msgstr[1] "({num} fechas más)"
msgstr[1] "({num} más fechas)"
#: pretix/static/pretixpresale/js/ui/cart.js:39
msgid "The items in your cart are no longer reserved for you."
msgstr "Los elementos en su carrito de compras han dejado de estar reservados."
msgstr "Los elementos en su carrito de compras ya no se encuentran reservados."
#: pretix/static/pretixpresale/js/ui/cart.js:41
msgid "Cart expired"
@@ -538,41 +510,33 @@ msgstr[1] ""
"Los elementos en su carrito de compras se han reservado por {num} minutos."
#: pretix/static/pretixpresale/js/ui/main.js:144
#, fuzzy
#| msgctxt "widget"
#| msgid "from %(currency)s %(price)s"
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr "a partir de %(currency)s %(price)s"
msgstr "El organizador se queda %(currency)s %(price)s"
#: pretix/static/pretixpresale/js/ui/main.js:152
#, fuzzy
#| msgctxt "widget"
#| msgid "from %(currency)s %(price)s"
msgid "You get %(currency)s %(amount)s back"
msgstr "a partir de %(currency)s %(price)s"
msgstr "Obtienes %(currency)s %(price)s de vuelta"
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr ""
msgstr "Por favor, ingrese el monto que el organizador puede quedarse."
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr "Por favor, introduce un valor para cada tipo de entrada."
msgstr "Por favor, introduzca un valor para cada tipo de entrada."
#: pretix/static/pretixpresale/js/ui/main.js:344
#, fuzzy
#| msgid "Cart expired"
#: pretix/static/pretixpresale/js/ui/main.js:345
msgid "required"
msgstr "El carrito de compras ha expirado"
msgstr "campo requerido"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr ""
msgstr "Zona horaria:"
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr ""
msgstr "Su hora local:"
#: pretix/static/pretixpresale/js/widget/widget.js:17
msgctxt "widget"
@@ -602,7 +566,7 @@ msgstr "GRATIS"
#: pretix/static/pretixpresale/js/widget/widget.js:22
msgctxt "widget"
msgid "from %(currency)s %(price)s"
msgstr "a partir de %(currency)s %(price)s"
msgstr "desde %(currency)s %(price)s"
#: pretix/static/pretixpresale/js/widget/widget.js:23
msgctxt "widget"
@@ -657,20 +621,19 @@ msgid ""
"There are currently a lot of users in this ticket shop. Please open the shop "
"in a new tab to continue."
msgstr ""
"Actualmente hay muchos usuarios en la tienda de tickets. Por favor abra la "
"tienda en una nueva pestaña para continuar."
#: pretix/static/pretixpresale/js/widget/widget.js:34
#, fuzzy
#| msgctxt "widget"
#| msgid "Close ticket shop"
msgctxt "widget"
msgid "Open ticket shop"
msgstr "Cerrar tienda de tickets"
msgstr "Abrir tienda de tickets"
#: pretix/static/pretixpresale/js/widget/widget.js:35
msgctxt "widget"
msgid "The cart could not be created. Please try again later"
msgstr ""
"El carrito de compras no ha podido crearse. Por favor, pruebe de nuevo más "
"El carrito de compras no ha podido crearse. Por favor, intente de nuevo más "
"tarde"
#: pretix/static/pretixpresale/js/widget/widget.js:36
@@ -679,6 +642,8 @@ msgid ""
"We could not create your cart, since there are currently too many users in "
"this ticket shop. Please click \"Continue\" to retry in a new tab."
msgstr ""
"No pudimos crear su carrito debido a que hay muchos usuarios en la tienda. "
"Por favor, presione \"Continuar\" para intentarlo en una nueva pestaña."
#: pretix/static/pretixpresale/js/widget/widget.js:38
msgctxt "widget"
@@ -697,17 +662,17 @@ msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:41
msgctxt "widget"
msgid "Resume checkout"
msgstr "Reanudar pago"
msgstr "Continuar pago"
#: pretix/static/pretixpresale/js/widget/widget.js:42
msgctxt "widget"
msgid "Redeem a voucher"
msgstr "Utilizar un cupón"
msgstr "Canjear un cupón"
#: pretix/static/pretixpresale/js/widget/widget.js:43
msgctxt "widget"
msgid "Redeem"
msgstr "Utilizar cupón"
msgstr "Canjear"
#: pretix/static/pretixpresale/js/widget/widget.js:44
msgctxt "widget"
@@ -732,7 +697,7 @@ msgstr "Ver variaciones"
#: pretix/static/pretixpresale/js/widget/widget.js:48
msgctxt "widget"
msgid "Choose a different event"
msgstr "Elige un evento diferente"
msgstr "Eligir un evento diferente"
#: pretix/static/pretixpresale/js/widget/widget.js:49
msgctxt "widget"
@@ -757,15 +722,12 @@ msgstr "Mes anterior"
#: pretix/static/pretixpresale/js/widget/widget.js:53
msgctxt "widget"
msgid "Next week"
msgstr ""
msgstr "Semana siguiente"
#: pretix/static/pretixpresale/js/widget/widget.js:54
#, fuzzy
#| msgctxt "widget"
#| msgid "Previous month"
msgctxt "widget"
msgid "Previous week"
msgstr "Mes anterior"
msgstr "Semana anterior"
#: pretix/static/pretixpresale/js/widget/widget.js:55
msgctxt "widget"
@@ -775,35 +737,35 @@ msgstr "Abrir selección de asientos"
#: pretix/static/pretixpresale/js/widget/widget.js:56
msgctxt "widget"
msgid "Load more"
msgstr ""
msgstr "Cargar más"
#: pretix/static/pretixpresale/js/widget/widget.js:58
msgid "Mo"
msgstr "Me"
msgstr "Lun"
#: pretix/static/pretixpresale/js/widget/widget.js:59
msgid "Tu"
msgstr "Ma"
msgstr "Mar"
#: pretix/static/pretixpresale/js/widget/widget.js:60
msgid "We"
msgstr "Mie"
msgstr "Mié"
#: pretix/static/pretixpresale/js/widget/widget.js:61
msgid "Th"
msgstr "Ju"
msgstr "Jue"
#: pretix/static/pretixpresale/js/widget/widget.js:62
msgid "Fr"
msgstr "Vi"
msgstr "Vie"
#: pretix/static/pretixpresale/js/widget/widget.js:63
msgid "Sa"
msgstr "Sá"
msgstr "Sáb"
#: pretix/static/pretixpresale/js/widget/widget.js:64
msgid "Su"
msgstr "Do"
msgstr "Dom"
#: pretix/static/pretixpresale/js/widget/widget.js:67
msgid "January"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2021-06-17 00:00+0000\n"
"Last-Translator: Jaakko Rinta-Filppula <jaakko@r-f.fi>\n"
"Language-Team: Finnish <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -476,10 +476,12 @@ msgid "Count"
msgstr "Määrä"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Kyllä"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "Ei"
@@ -515,22 +517,22 @@ msgstr ""
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "Ostoskori on vanhentunut"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr "Aikavyöhyke:"
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: French\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2020-09-15 17:00+0000\n"
"Last-Translator: Martin Gross <martin@pc-coholic.de>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -506,10 +506,12 @@ msgid "Count"
msgstr "Compter"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Oui"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "Non"
@@ -551,22 +553,22 @@ msgstr "de %(currency)s %(price)s"
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr "SVP entrez une quantité pour un de vos types de billets."
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "Panier expiré"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,794 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2021-09-24 13:54+0000\n"
"Last-Translator: ofirtro <ofir.tro@gmail.com>\n"
"Language-Team: Hebrew <https://translate.pretix.eu/projects/pretix/pretix-js/"
"he/>\n"
"Language: he\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && "
"n % 10 == 0) ? 2 : 3));\n"
"X-Generator: Weblate 4.8\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:68
msgid "Marked as paid"
msgstr "סומן כשולם"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:76
msgid "Comment:"
msgstr "תגובה:"
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
msgid "Placed orders"
msgstr "הזמנות שבוצעו"
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
msgid "Paid orders"
msgstr "הזמנות ששולמו"
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
msgid "Total revenue"
msgstr "הכנסה כוללת"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
msgid "Contacting Stripe …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
msgid "Total"
msgstr "סה\"כ"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
msgid "Confirming your payment …"
msgstr "מאמת את התשלום שלך…"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
msgid "Contacting your bank …"
msgstr "יוצר קשר עם הבנק שלך…"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:30
msgid "Select a check-in list"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:31
msgid "No active check-in lists found."
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:32
msgid "Switch check-in list"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:33
msgid "Search results"
msgstr "חיפוש"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:34
msgid "No tickets found"
msgstr "לא נמצאו כרטיסים"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:35
msgid "Result"
msgstr "תוצאות"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:36
msgid "This ticket requires special attention"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:37
msgid "Switch direction"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:38
msgid "Entry"
msgstr "כניסה"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:39
msgid "Exit"
msgstr "יציאה"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:40
msgid "Scan a ticket or search and press return…"
msgstr "סרוק כרטיס או חפש…"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:41
msgid "Load more"
msgstr "טען עוד"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:42
msgid "Valid"
msgstr "תקף"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:43
msgid "Unpaid"
msgstr "לא שולם"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:44
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:45
msgid "Canceled"
msgstr "בוטל"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:46
msgid "Redeemed"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:47
msgid "Cancel"
msgstr "בטל"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "המשך"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:49
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58
msgid "Ticket not paid"
msgstr "כרטיס לא שולם"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:50
msgid "This ticket is not yet paid. Do you want to continue anyways?"
msgstr "הכרטיס עדיין לא שולם. רוצה להמשיך בכל זאת?"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
msgid "Additional information required"
msgstr "מידע נוסף"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
msgid "Valid ticket"
msgstr "כרטיס תקף"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:53
msgid "Exit recorded"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:54
msgid "Ticket already used"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:55
msgid "Information required"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56
msgid "Unknown ticket"
msgstr "כרטיס לא מזוהה"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57
msgid "Ticket type not allowed here"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:59
msgid "Entry not allowed"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
msgid "Ticket code revoked/changed"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
msgid "Order canceled"
msgstr "הזמנה בוטלה"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
msgid "Checked-in Tickets"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
msgid "Valid Tickets"
msgstr "כרטיסים תקפים"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
msgid "Currently inside"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:43
#: pretix/static/pretixbase/js/asynctask.js:119
msgid ""
"Your request is currently being processed. Depending on the size of your "
"event, this might take up to a few minutes."
msgstr "הבקשה שלך מתבצעת ויכולה לקחת כמה דקות בהתאם לגודל האירוע."
#: pretix/static/pretixbase/js/asynctask.js:48
#: pretix/static/pretixbase/js/asynctask.js:124
msgid "Your request has been queued on the server and will soon be processed."
msgstr "הבקשה שלך תבוצע בהקדם."
#: pretix/static/pretixbase/js/asynctask.js:54
#: pretix/static/pretixbase/js/asynctask.js:130
msgid ""
"Your request arrived on the server but we still wait for it to be processed. "
"If this takes longer than two minutes, please contact us or go back in your "
"browser and try again."
msgstr ""
"הבקשה שלך הגיעה לשרת אבל עדיין לא התחילה. אם זה לוקח יותר משתי דקות, אנא צור "
"איתנו קשר או נסה שנית."
#: pretix/static/pretixbase/js/asynctask.js:89
#: pretix/static/pretixbase/js/asynctask.js:175
#: pretix/static/pretixbase/js/asynctask.js:180
#: pretix/static/pretixcontrol/js/ui/mail.js:24
msgid "An error of type {code} occurred."
msgstr "שגיאה {code} התרחשה."
#: pretix/static/pretixbase/js/asynctask.js:92
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
msgstr "אנחנו לא מצליחים לגשת לשרת, אבל ממשיכים לנסות. שגיאה אחרונה: {code}"
#: pretix/static/pretixbase/js/asynctask.js:144
#: pretix/static/pretixcontrol/js/ui/mail.js:21
msgid "The request took too long. Please try again."
msgstr "הבקשה לקחה יותר מידי זמן. נסה שנית."
#: pretix/static/pretixbase/js/asynctask.js:183
#: pretix/static/pretixcontrol/js/ui/mail.js:26
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
msgstr "אירעה שגיאה. אנא נסה שנית. שגיאה: {code}"
#: pretix/static/pretixbase/js/asynctask.js:205
msgid "We are processing your request …"
msgstr "הבקשה שלך מתבצעת…"
#: pretix/static/pretixbase/js/asynctask.js:213
msgid ""
"We are currently sending your request to the server. If this takes longer "
"than one minute, please check your internet connection and then reload this "
"page and try again."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:262
#: pretix/static/pretixcontrol/js/ui/main.js:34
msgid "Close message"
msgstr ""
#: pretix/static/pretixcontrol/js/clipboard.js:23
msgid "Copied!"
msgstr ""
#: pretix/static/pretixcontrol/js/clipboard.js:29
msgid "Press Ctrl-C to copy!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:10
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:16
msgid "is one of"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:22
msgid "is before"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:26
msgid "is after"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:59
msgid "Product"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:63
msgid "Product variation"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:67
msgid "Current date and time"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:71
msgid "Number of previous entries"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:75
msgid "Number of previous entries since midnight"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:79
msgid "Number of days with a previous entry"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:97
msgid "All of the conditions below (AND)"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:98
msgid "At least one of the conditions below (OR)"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:99
msgid "Event start"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:100
msgid "Event end"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:101
msgid "Event admission"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:102
msgid "custom date and time"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:103
msgid "custom time"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:104
msgid "Tolerance (minutes)"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:105
msgid "Add condition"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:106
msgid "minutes"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:69
msgid "Lead Scan QR"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:71
msgid "Check-in QR"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:313
msgid "The PDF background file could not be loaded for the following reason:"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:521
msgid "Group of objects"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:527
msgid "Text object"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:529
msgid "Barcode area"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:531
msgid "Image area"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:533
msgid "Powered by pretix"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:535
msgid "Object"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:539
msgid "Ticket design"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:813
msgid "Saving failed."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:862
#: pretix/static/pretixcontrol/js/ui/editor.js:901
msgid "Error while uploading your PDF file, please try again."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:886
msgid "Do you really want to leave the editor without saving your changes?"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/mail.js:19
msgid "An error has occurred."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/mail.js:54
msgid "Generating messages …"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:70
msgid "Unknown error."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:271
msgid "Your color has great contrast and is very easy to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:275
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:279
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:417
msgid "All"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:418
msgid "None"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:419
msgid "Search query"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:422
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:834
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:870
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:911
msgid "You have unsaved changes!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/orderchange.js:25
msgid "Calculating default price…"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:42
msgid "Others"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:82
msgid "Count"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/subevent.js:111
msgid "(one more date)"
msgid_plural "({num} more dates)"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
#: pretix/static/pretixpresale/js/ui/cart.js:39
msgid "The items in your cart are no longer reserved for you."
msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:41
msgid "Cart expired"
msgstr ""
#: pretix/static/pretixpresale/js/ui/cart.js:46
msgid "The items in your cart are reserved for you for one minute."
msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
#: pretix/static/pretixpresale/js/ui/main.js:144
msgid "The organizer keeps %(currency)s %(amount)s"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:152
msgid "You get %(currency)s %(amount)s back"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:168
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:345
msgid "required"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:17
msgctxt "widget"
msgid "Sold out"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:18
msgctxt "widget"
msgid "Buy"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:19
msgctxt "widget"
msgid "Register"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:20
msgctxt "widget"
msgid "Reserved"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:21
msgctxt "widget"
msgid "FREE"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:22
msgctxt "widget"
msgid "from %(currency)s %(price)s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:23
msgctxt "widget"
msgid "incl. %(rate)s% %(taxname)s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:24
msgctxt "widget"
msgid "plus %(rate)s% %(taxname)s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:25
msgctxt "widget"
msgid "incl. taxes"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:26
msgctxt "widget"
msgid "plus taxes"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:27
#, javascript-format
msgctxt "widget"
msgid "currently available: %s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:28
msgctxt "widget"
msgid "Only available with a voucher"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:29
#, javascript-format
msgctxt "widget"
msgid "minimum amount to order: %s"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:30
msgctxt "widget"
msgid "Close ticket shop"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:31
msgctxt "widget"
msgid "The ticket shop could not be loaded."
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:32
msgctxt "widget"
msgid ""
"There are currently a lot of users in this ticket shop. Please open the shop "
"in a new tab to continue."
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:34
msgctxt "widget"
msgid "Open ticket shop"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:35
msgctxt "widget"
msgid "The cart could not be created. Please try again later"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:36
msgctxt "widget"
msgid ""
"We could not create your cart, since there are currently too many users in "
"this ticket shop. Please click \"Continue\" to retry in a new tab."
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:38
msgctxt "widget"
msgid "Waiting list"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:39
msgctxt "widget"
msgid ""
"You currently have an active cart for this event. If you select more "
"products, they will be added to your existing cart."
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:41
msgctxt "widget"
msgid "Resume checkout"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:42
msgctxt "widget"
msgid "Redeem a voucher"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:43
msgctxt "widget"
msgid "Redeem"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:44
msgctxt "widget"
msgid "Voucher code"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:45
msgctxt "widget"
msgid "Close"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:46
msgctxt "widget"
msgid "Continue"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:47
msgctxt "widget"
msgid "See variations"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:48
msgctxt "widget"
msgid "Choose a different event"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:49
msgctxt "widget"
msgid "Choose a different date"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:50
msgctxt "widget"
msgid "Back"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:51
msgctxt "widget"
msgid "Next month"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:52
msgctxt "widget"
msgid "Previous month"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:53
msgctxt "widget"
msgid "Next week"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:54
msgctxt "widget"
msgid "Previous week"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:55
msgctxt "widget"
msgid "Open seat selection"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:56
msgctxt "widget"
msgid "Load more"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:58
msgid "Mo"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:59
msgid "Tu"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:60
msgid "We"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:61
msgid "Th"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:62
msgid "Fr"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:63
msgid "Sa"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:64
msgid "Su"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:67
msgid "January"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:68
msgid "February"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:69
msgid "March"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:70
msgid "April"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:71
msgid "May"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:72
msgid "June"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:73
msgid "July"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:74
msgid "August"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:75
msgid "September"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:76
msgid "October"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:77
msgid "November"
msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:78
msgid "December"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2020-01-24 08:00+0000\n"
"Last-Translator: Prokaj Miklós <mixolid0@gmail.com>\n"
"Language-Team: Hungarian <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -501,10 +501,12 @@ msgid "Count"
msgstr "Számítás"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Igen"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "Nem"
@@ -546,22 +548,22 @@ msgstr "%(currency) %(price)-tól"
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr "Adjon meg egy mennyiséget az egyik jegytípusból."
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "A kosár lejárt"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2021-07-19 11:57+0000\n"
"Last-Translator: dedecosta <dedecosta2@live.it>\n"
"Language-Team: Italian <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -473,10 +473,12 @@ msgid "Count"
msgstr "Conteggio"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Si"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "No"
@@ -512,20 +514,20 @@ msgstr "Ricevi indietro %(currency)s %(amount)s"
msgid "Please enter the amount the organizer can keep."
msgstr "Inserisci l'importo che l'organizzatore può trattenere."
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr "Inserisci la quantità per una tipologia di biglietto."
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
msgid "required"
msgstr "richiesto"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr "Fuso orario:"
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr "Ora locale:"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2019-11-13 06:00+0000\n"
"Last-Translator: Zane Smite <z.smite@riga-jurmala.com>\n"
"Language-Team: Latvian <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -509,10 +509,12 @@ msgid "Count"
msgstr "Skaits"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Jā"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "Nē"
@@ -556,22 +558,22 @@ msgstr "no %(currency)s %(price)s"
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr "Lūdzu, ievadiet nepieciešamo daudzumu izvēlētajam biļešu veidam."
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
#, fuzzy
#| msgid "Cart expired"
msgid "required"
msgstr "Pirkumu groza rezervācijas laiks ir beidzies"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2021-05-31 11:26+0000\n"
"Last-Translator: zackern <zacker@zacker.no>\n"
"Language-Team: Norwegian Bokmål <https://translate.pretix.eu/projects/pretix/"
@@ -470,10 +470,12 @@ msgid "Count"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr ""
@@ -509,20 +511,20 @@ msgstr ""
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
msgid "required"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: 2021-08-05 04:00+0000\n"
"Last-Translator: Maarten van den Berg <maartenberg1@gmail.com>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -472,10 +472,12 @@ msgid "Count"
msgstr "Aantal"
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr "Ja"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr "Nee"
@@ -512,20 +514,20 @@ msgstr "U krijgt %(currency)s %(amount)s terug"
msgid "Please enter the amount the organizer can keep."
msgstr "Voer het bedrag in dat de organisator mag houden."
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr "Voer een hoeveelheid voor een van de producten in."
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
msgid "required"
msgstr "verplicht"
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr "Tijdzone:"
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr "Uw lokale tijd:"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-27 13:05+0000\n"
"POT-Creation-Date: 2021-09-27 18:49+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -457,10 +457,12 @@ msgid "Count"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:136
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "Yes"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:269
msgid "No"
msgstr ""
@@ -496,20 +498,20 @@ msgstr ""
msgid "Please enter the amount the organizer can keep."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:308
#: pretix/static/pretixpresale/js/ui/main.js:309
msgid "Please enter a quantity for one of the ticket types."
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:344
#: pretix/static/pretixpresale/js/ui/main.js:345
msgid "required"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:441
#: pretix/static/pretixpresale/js/ui/main.js:459
#: pretix/static/pretixpresale/js/ui/main.js:447
#: pretix/static/pretixpresale/js/ui/main.js:465
msgid "Time zone:"
msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:450
#: pretix/static/pretixpresale/js/ui/main.js:456
msgid "Your local time:"
msgstr ""

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