Compare commits

..

80 Commits

Author SHA1 Message Date
Raphael Michel
cb3f3f5084 Advertise pretixSCAN 2019-07-18 17:26:49 +02:00
Raphael Michel
85edbe4837 Improved device validation 2019-07-18 17:26:34 +02:00
Raphael Michel
6d12b3780c Allow to hide all sold out items 2019-07-18 15:01:33 +02:00
Raphael Michel
a99616b1e0 API: Check-in response code for canceled 2019-07-18 15:01:33 +02:00
Martin Gross
a5ba7440fe Fix #1345 - Only enable payment button once Stripe Elements are ready 2019-07-16 15:41:37 +02:00
Raphael Michel
a02ea45dba Allow quotas to "close" when once full (#1344)
* Model

* Some UI

* API and logging

* Permission check

* Add tests

* Move option around
2019-07-16 14:02:27 +02:00
Raphael Michel
c1e2fb36ba Auto-expand variation description when variation is selected 2019-07-16 11:53:43 +02:00
Raphael Michel
b67c684969 Revert "Allow to show description of add-on product variations by default"
This reverts commit 8d674965d1.
2019-07-16 11:46:11 +02:00
Raphael Michel
dc42dbb837 Allow to use a selection for name titles 2019-07-16 10:23:43 +02:00
Raphael Michel
44ffc0685e Show date_to in PDF variable "event_date_range" regardless of event settings
Z#2349533
2019-07-16 09:31:40 +02:00
Raphael Michel
a79a156a28 Show preview of answered images 2019-07-16 09:31:36 +02:00
Raphael Michel
fb1f6c65af Display invoices as inline PDF
They are not user-controllable enough to cause any harm here
2019-07-16 09:16:33 +02:00
Raphael Michel
8d674965d1 Allow to show description of add-on product variations by default 2019-07-15 11:26:42 +02:00
Raphael Michel
020122b44f Fix missing words 2019-07-15 11:01:33 +02:00
Raphael Michel
f55fff6495 Update from Weblate (#1342)
Update from Weblate
2019-07-15 11:01:31 +02:00
Raphael Michel
08316129d3 Translated on translate.pretix.eu (German (informal))
Currently translated at 100.0% (3165 of 3165 strings)

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

powered by weblate
2019-07-15 09:00:55 +00:00
Raphael Michel
a39563aa3e Translated on translate.pretix.eu (German)
Currently translated at 99.9% (3164 of 3165 strings)

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

powered by weblate
2019-07-15 09:00:54 +00:00
Raphael Michel
a3707a962b Fix problems with CartMixin on empty order
Fix PRETIXEU-18A
2019-07-15 10:46:38 +02:00
Raphael Michel
4bb8c3991e Fix badge-creation task
PRETIXEU-150
2019-07-15 10:46:27 +02:00
Raphael Michel
0d5c2f6329 Update po files
[CI skip]

Signed-off-by: Raphael Michel <mail@raphaelmichel.de>
2019-07-15 10:31:30 +02:00
Raphael Michel
17c0cfb395 Add signal: order_split 2019-07-15 10:30:44 +02:00
Raphael Michel
e55f0cdf11 Retire make_testdata.py 2019-07-14 17:55:51 +02:00
Christian González
a2fbc376a5 typo in comment (#1339) 2019-07-14 16:59:08 +02:00
Raphael Michel
be310a4e47 Docs: Add agenda plugin to structure guide 2019-07-12 13:28:46 +02:00
Raphael Michel
35037c79cc Add signal validate_cart_addons 2019-07-12 13:06:29 +02:00
Raphael Michel
f8bb139651 AddOnsForm: Already validate min_count/max_count 2019-07-12 12:32:43 +02:00
Raphael Michel
77046136f2 asynctask.js: Hack to allow form validation 2019-07-12 12:23:34 +02:00
Raphael Michel
53a0d62d93 Allow dependent questions to depend on multiple values (#1336) 2019-07-11 13:32:45 +02:00
Raphael Michel
d994fc674a Do not CASCADE-delete vouchers when deleting items or quotas 2019-07-11 12:35:52 +02:00
Raphael Michel
f066ed01ff Show event meta data in backend list of events 2019-07-11 11:16:36 +02:00
Raphael Michel
fb66434fc9 Update from Weblate (#1335)
Update from Weblate
2019-07-11 10:37:46 +02:00
Vitor Piedras
3f9269f6e5 Translated on translate.pretix.eu (Portuguese (Brazil))
Currently translated at 15.9% (501 of 3152 strings)

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

powered by weblate
2019-07-11 06:56:27 +00:00
Raphael Michel
2a30a1a039 Translated on translate.pretix.eu (German (informal))
Currently translated at 100.0% (3152 of 3152 strings)

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

powered by weblate
2019-07-11 06:56:27 +00:00
Raphael Michel
846f20692d Translated on translate.pretix.eu (German)
Currently translated at 100.0% (3152 of 3152 strings)

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

powered by weblate
2019-07-11 06:56:27 +00:00
Raphael Michel
2eb5adb6c1 Stripe: Improve exception handling
PRETIXEU-17Y
2019-07-11 08:56:01 +02:00
Raphael Michel
491753008d Introduce Item.show_quota_left 2019-07-10 16:08:21 +02:00
Raphael Michel
6d6cd3b7cf [SECURITY] Fix XSS in global admin mode 2019-07-10 14:52:58 +02:00
Raphael Michel
eaf6da7272 Protect against main javascript being loaded before translations 2019-07-10 14:31:49 +02:00
Raphael Michel
22ce7a388d Do not send notifications to disabled users 2019-07-10 09:00:41 +02:00
Raphael Michel
e687eee9f1 Widget: Allow voucher with itemless button 2019-07-09 18:55:10 +02:00
Raphael Michel
e7baca952b Fix voucher queryset (again) 2019-07-09 18:02:08 +02:00
Raphael Michel
eef713816e Sort keys in JSON payment metadata 2019-07-09 16:13:37 +02:00
Raphael Michel
5a03033255 Add utility to get IP address 2019-07-09 16:13:37 +02:00
Raphael Michel
59daeba477 Do not redirect to order.pay.complete for pending orders 2019-07-09 16:13:37 +02:00
Raphael Michel
c1a4b8d343 Payment provider API: Add payment argument to render_invoice_text and order_pending_mail_render 2019-07-09 16:13:37 +02:00
Raphael Michel
0ac98f5127 Use inspect instead of TypeError for backwards-compatible APIs 2019-07-09 16:13:37 +02:00
Raphael Michel
55d423af18 Widget: Allow to filter by attributes 2019-07-08 23:27:46 +02:00
Raphael Michel
285694955c Fix AttributeError 2019-07-08 18:25:31 +02:00
Raphael Michel
2352f3b811 Fix voucher validation in CartManager 2019-07-08 17:50:22 +02:00
Raphael Michel
08bfe13dc3 Re-add validation for hidden vouchers 2019-07-08 14:25:35 +02:00
Raphael Michel
ec522ed7e5 Tax list exporter as Excel 2019-07-08 14:25:22 +02:00
Raphael Michel
197ec84f05 Order overview: Allow to filter by date 2019-07-08 14:25:22 +02:00
Martin Gross
42af8b1602 Remove excessive chars in U2F_GET_API_VERSION_RESPONSE 2019-07-08 13:39:12 +02:00
Raphael Michel
f6a4c5271e Remove obsolete validation 2019-07-08 11:05:08 +02:00
Martin Gross
fb53beee2d Option to notify users when questions have been changed in backend 2019-07-08 10:23:32 +02:00
Raphael Michel
ca1c387a41 Allow quota-level vouchers for hidden products (#1123)
* Changes in checks

* Backwards-compatible implementation

* Add test

* Fix voucher bulk form
2019-07-07 13:36:04 +02:00
Raphael Michel
5180b5e48b Fix #1329 -- Fix image lightbox for products with variations 2019-07-05 16:58:39 +02:00
Raphael Michel
a5e94bf63f Protect against fee signal returning None 2019-07-05 14:33:43 +02:00
Raphael Michel
09ef7aac6e Subevent: Allow to pass empty mapping 2019-07-04 18:25:48 +02:00
Raphael Michel
d90510a1bd Fix incorrect headline 2019-07-04 17:59:25 +02:00
Raphael Michel
48790e7743 Fix incorrect header in documentation samples 2019-07-04 17:59:17 +02:00
Raphael Michel
cbeaf399df Update Stripe API 2019-07-04 11:08:05 +02:00
Raphael Michel
779a3698a8 Catch general HTTP errors during VAT validation 2019-07-04 10:39:41 +02:00
Raphael Michel
a5e2caf438 Consistently include other fees in percentual payment fee 2019-07-04 09:31:21 +02:00
Martin Gross
ce79769293 Fix overlooked Stripe-Tests, still using _token instead of _payment_method_id 2019-07-03 22:04:05 +02:00
Martin Gross
9fbb8fa781 Do not _handle_payment_intent() in Stripe's pending order view 2019-07-03 19:19:40 +02:00
Raphael Michel
83c551c1ba API: Correctly set default position IDs for orders 2019-07-03 16:46:03 +02:00
Raphael Michel
328cd9bdc5 Use shell_plus in shell_scoped 2019-07-03 14:32:07 +02:00
Raphael Michel
4ce7655958 Docs: Remove experimental note from order creation endpoint 2019-07-03 13:39:43 +02:00
Raphael Michel
bccc73f1dc Optimized command-line exports 2019-07-03 13:35:26 +02:00
Raphael Michel
5eeba88283 Stripe: Robust webhook recognition 2019-07-03 10:57:36 +02:00
Raphael Michel
4c2fe9fc20 Update from Weblate (#1326)
Update from Weblate
2019-07-02 12:41:13 +02:00
Maarten van den Berg
f2ba409b03 Translated on translate.pretix.eu (Dutch (informal))
Currently translated at 100.0% (3152 of 3152 strings)

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

powered by weblate
2019-07-02 10:37:14 +00:00
Maarten van den Berg
296c2b6e28 Translated on translate.pretix.eu (Dutch (informal))
Currently translated at 100.0% (100 of 100 strings)

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

powered by weblate
2019-07-02 10:37:14 +00:00
Maarten van den Berg
ab27bcca42 Translated on translate.pretix.eu (Dutch (informal))
Currently translated at 99.9% (3151 of 3152 strings)

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

powered by weblate
2019-07-02 10:37:14 +00:00
Maarten van den Berg
b0a365a099 Translated on translate.pretix.eu (Dutch)
Currently translated at 100.0% (3152 of 3152 strings)

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

powered by weblate
2019-07-02 10:37:14 +00:00
Maarten van den Berg
97fc095d20 Translated on translate.pretix.eu (Dutch)
Currently translated at 100.0% (100 of 100 strings)

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

powered by weblate
2019-07-02 10:37:14 +00:00
Maarten van den Berg
cfb1cd8fdb Translated on translate.pretix.eu (Dutch)
Currently translated at 100.0% (3152 of 3152 strings)

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

powered by weblate
2019-07-02 10:37:14 +00:00
Martin Gross
446cf68377 Stripe SCA (#1275)
* Stripe SCA
- Upgrade to latest Stripe API
- Deprecate Stripe Checkout for CC
- Migrate CC payments to Payment Intents

* Move SCA to its own view

* Handle CardErrors for PaymentIntents

* Abilty to handle charge webhooks with PaymentIntents

* Better handling of Stripe References

* Fix Stripe Tests

* Move SCA page into orderlayout; perform iFrame SCA

* Handle disputes and pi-webhooks better, fill more into ReferencedStripeObject

* Optionally pass prefetched PaymentIntent to handle-func

* Fix style

* Send message to window.parent not window.top (widget compatibility)

* More accurate loading message

* Show a cog on sca_return.html. On a good internet connection, you barely see it, but on a bad one…

* Robust error handling

* If it's a method and used like a method, let's actually call it like a method!

* Remove logging statement

* Fix JavaScript interference with other frame events

* Use 4:3 aspect ratio, but at least 600px

* Adjust to django_scopes
2019-07-02 12:37:07 +02:00
Raphael Michel
b727207e79 API: Fix query for check-in list status 2019-07-01 17:18:22 +02:00
179 changed files with 25150 additions and 21079 deletions

View File

@@ -82,6 +82,11 @@ Example::
Enables or disables obligatory usage of Two-Factor Authentication for users of the pretix backend.
Defaults to ``False``
``trust_x_forwarded_for``
Specifies whether the ``X-Forwarded-For`` header can be trusted. Only set to ``on`` if you have a reverse
proxy that actively removes and re-adds the header to make sure the correct client IP is the first value.
Defaults to ``off``.
Locale settings
---------------

View File

@@ -207,7 +207,7 @@ Cart position endpoints
POST /api/v1/organizers/bigevents/events/sampleconf/cartpositions/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"item": 1,

View File

@@ -131,7 +131,7 @@ Endpoints
POST /api/v1/organizers/bigevents/events/sampleconf/categories/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"name": {"en": "Tickets"},

View File

@@ -209,7 +209,7 @@ Endpoints
POST /api/v1/organizers/bigevents/events/sampleconf/checkinlists/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"name": "VIP entry",
@@ -548,6 +548,8 @@ Order position endpoints
you do not implement question handling in your user interface, you **must**
set this to ``false``. In that case, questions will just be ignored. Defaults
to ``true``.
:<json boolean canceled_supported: When this parameter is set to ``true``, the response code ``canceled`` may be
returned. Otherwise, canceled orders will return ``unpaid``.
:<json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
:<json boolean force: Specifies that the check-in should succeed regardless of previous check-ins or required
questions that have not been filled. Defaults to ``false``.
@@ -576,6 +578,7 @@ Order position endpoints
"nonce": "Pvrk50vUzQd0DhdpNRL4I4OcXsvg70uA",
"datetime": null,
"questions_supported": true,
"canceled_supported": true,
"answers": {
"4": "XS"
}
@@ -659,7 +662,9 @@ Order position endpoints
Possible error reasons:
* ``unpaid`` - Ticket is not paid for or has been refunded
* ``unpaid`` - Ticket is not paid for
* ``canceled`` Ticket is canceled or expired. This reason is only sent when your request sets
``canceled_supported`` to ``true``, otherwise these orders return ``unpaid``.
* ``already_redeemed`` - Ticket already has been redeemed
* ``product`` - Tickets with this product may not be scanned at this device

View File

@@ -203,7 +203,7 @@ Endpoints
POST /api/v1/organizers/bigevents/events/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"name": {"en": "Sample Conference"},
@@ -285,7 +285,7 @@ Endpoints
POST /api/v1/organizers/bigevents/events/sampleconf/clone/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"name": {"en": "Sample Conference"},
@@ -362,7 +362,7 @@ Endpoints
PATCH /api/v1/organizers/bigevents/events/sampleconf/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"plugins": [

View File

@@ -134,7 +134,7 @@ Endpoints
POST /api/v1/organizers/(organizer)/events/(event)/items/(item)/addons/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"addon_category": 1,

View File

@@ -134,7 +134,7 @@ Endpoints
POST /api/v1/organizers/(organizer)/events/(event)/items/(item)/bundles/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"bundled_item": 3,

View File

@@ -152,7 +152,7 @@ Endpoints
POST /api/v1/organizers/bigevents/events/sampleconf/items/1/variations/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"value": {"en": "Student"},

View File

@@ -72,6 +72,8 @@ generate_tickets boolean If ``false``, t
non-admission or add-on product, regardless of event
settings. If this is ``null``, regular ticketing
rules apply.
show_quota_left boolean Publicly show how many tickets are still available.
If this is ``null``, the event default is used.
has_variations boolean Shows whether or not this item has variations.
variations list of objects A list with one object for each variation of this item.
Can be empty. Only writable during creation,
@@ -142,6 +144,10 @@ bundles list of objects Definition of b
The ``bundles`` and ``require_bundling`` attributes have been added.
.. versionchanged:: 3.0
The ``show_quota_left`` attribute has been added.
Notes
-----
@@ -207,6 +213,7 @@ Endpoints
"checkin_attention": false,
"has_variations": false,
"generate_tickets": null,
"show_quota_left": null,
"require_approval": false,
"require_bundling": false,
"variations": [
@@ -294,6 +301,7 @@ Endpoints
"hide_without_voucher": false,
"allow_cancel": true,
"generate_tickets": null,
"show_quota_left": null,
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
@@ -342,7 +350,7 @@ Endpoints
POST /api/v1/organizers/bigevents/events/sampleconf/items/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"id": 1,
@@ -366,6 +374,7 @@ Endpoints
"hide_without_voucher": false,
"allow_cancel": true,
"generate_tickets": null,
"show_quota_left": null,
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
@@ -427,6 +436,7 @@ Endpoints
"min_per_order": null,
"max_per_order": null,
"generate_tickets": null,
"show_quota_left": null,
"checkin_attention": false,
"has_variations": true,
"require_approval": false,
@@ -515,6 +525,7 @@ Endpoints
"require_voucher": false,
"hide_without_voucher": false,
"generate_tickets": null,
"show_quota_left": null,
"allow_cancel": true,
"min_per_order": null,
"max_per_order": null,

View File

@@ -698,8 +698,6 @@ Creating orders
Creates a new order.
.. warning:: This endpoint is considered **experimental**. It might change at any time without prior notice.
.. warning::
This endpoint is intended for advanced users. It is not designed to be used to build your own shop frontend,

View File

@@ -54,11 +54,12 @@ dependency_question integer Internal ID of
this attribute is set to the value given in
``dependency_value``. This cannot be combined with
``ask_during_checkin``.
dependency_value string The value ``dependency_question`` needs to be set to.
If ``dependency_question`` is set to a boolean
question, this should be ``"true"`` or ``"false"``.
Otherwise, it should be the ``identifier`` of a
question option.
dependency_values list of strings If ``dependency_question`` is set to a boolean
question, this should be ``["True"]`` or ``["False"]``.
Otherwise, it should be a list of ``identifier`` values
of question options.
dependency_value string An old version of ``dependency_values`` that only allows
for one value. **Deprecated.**
===================================== ========================== =======================================================
.. versionchanged:: 1.12
@@ -75,6 +76,10 @@ dependency_value string The value ``dep
The attribute ``hidden`` and the question type ``CC`` have been added.
.. versionchanged:: 3.0
The attribute ``dependency_values`` has been added.
Endpoints
---------
@@ -120,6 +125,7 @@ Endpoints
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"dependency_values": [],
"options": [
{
"id": 1,
@@ -188,6 +194,7 @@ Endpoints
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"dependency_values": [],
"options": [
{
"id": 1,
@@ -228,7 +235,7 @@ Endpoints
POST /api/v1/organizers/bigevents/events/sampleconf/questions/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"question": {"en": "T-Shirt size"},
@@ -239,7 +246,7 @@ Endpoints
"ask_during_checkin": false,
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"dependency_values": [],
"options": [
{
"answer": {"en": "S"}
@@ -274,6 +281,7 @@ Endpoints
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"dependency_values": [],
"options": [
{
"id": 1,
@@ -346,6 +354,7 @@ Endpoints
"hidden": false,
"dependency_question": null,
"dependency_value": null,
"dependency_values": [],
"options": [
{
"id": 1,

View File

@@ -20,12 +20,22 @@ size integer The size of the
items list of integers List of item IDs this quota acts on.
variations list of integers List of item variation IDs this quota acts on.
subevent integer ID of the date inside an event series this quota belongs to (or ``null``).
close_when_sold_out boolean If ``true``, the quota will "close" as soon as it is
sold out once. Even if tickets become available again,
they will not be sold unless the quota is set to open
again.
closed boolean Whether the quota is currently closed (see above
field).
===================================== ========================== =======================================================
.. versionchanged:: 1.10
The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added.
.. versionchanged:: 3.0
The attributes ``close_when_sold_out`` and ``closed`` have been added.
Endpoints
---------
@@ -61,7 +71,9 @@ Endpoints
"size": 200,
"items": [1, 2],
"variations": [1, 4, 5, 7],
"subevent": null
"subevent": null,
"close_when_sold_out": false,
"closed": false
}
]
}
@@ -102,7 +114,9 @@ Endpoints
"size": 200,
"items": [1, 2],
"variations": [1, 4, 5, 7],
"subevent": null
"subevent": null,
"close_when_sold_out": false,
"closed": false
}
:param organizer: The ``slug`` field of the organizer to fetch
@@ -123,14 +137,16 @@ Endpoints
POST /api/v1/organizers/bigevents/events/sampleconf/quotas/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"name": "Ticket Quota",
"size": 200,
"items": [1, 2],
"variations": [1, 4, 5, 7],
"subevent": null
"subevent": null,
"close_when_sold_out": false,
"closed": false
}
**Example response**:
@@ -147,7 +163,9 @@ Endpoints
"size": 200,
"items": [1, 2],
"variations": [1, 4, 5, 7],
"subevent": null
"subevent": null,
"close_when_sold_out": false,
"closed": false
}
:param organizer: The ``slug`` field of the organizer of the event/item to create a quota for
@@ -200,7 +218,9 @@ Endpoints
1,
2
],
"subevent": null
"subevent": null,
"close_when_sold_out": false,
"closed": false
}
:param organizer: The ``slug`` field of the organizer to modify

View File

@@ -109,7 +109,7 @@ Endpoints
POST /api/v1/organizers/bigevents/seatingplans/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"name": "Main plan",

View File

@@ -140,7 +140,7 @@ Endpoints
POST /api/v1/organizers/bigevents/events/sampleconf/subevents/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"name": {"en": "First Sample Conference"},
@@ -271,7 +271,7 @@ Endpoints
PATCH /api/v1/organizers/bigevents/events/sampleconf/subevents/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"name": {"en": "New Subevent Name"},

View File

@@ -137,7 +137,7 @@ Endpoints
POST /api/v1/organizers/bigevents/webhooks/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
Content-Type: application/json
{
"enabled": true,

View File

@@ -20,7 +20,7 @@ Order events
There are multiple signals that will be sent out in the ordering cycle:
.. automodule:: pretix.base.signals
:members: validate_cart, validate_order, order_fee_calculation, order_paid, order_placed, order_canceled, order_expired, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download
:members: validate_cart, validate_cart_addons, validate_order, order_fee_calculation, order_paid, order_placed, order_canceled, order_expired, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download, order_split
Frontend
--------

View File

@@ -65,9 +65,7 @@ Then, create the local database::
python manage.py migrate
A first user with username ``admin@localhost`` and password ``admin`` will be automatically
created. If you want to generate more test data, run::
python make_testdata.py
created.
If you want to see pretix in a different language than English, you have to compile our language
files::

View File

@@ -85,8 +85,14 @@ Use case: Conference with workshops
When running a conference, you might also organize a number of workshops with smaller capacity. To be able to plan, it would be great to know which workshops an attendee plans to attend.
Option A: Questions
"""""""""""""""""""
Your first and simplest option is to just create a multiple-choice question. This has the upside of making it easy for users to change their mind later on, but will not allow you to restrict the number of attendees signing up for a given workshop or even charge extra for a given workshop.
Option B: Add-on products with fixed time slots
"""""""""""""""""""""""""""""""""""""""""""""""
The usually better option is to go with add-on products. Let's take for example the following conference schedule, in which the lecture can be attended by anyone, but the workshops only have space for 20 persons each:
==================== =================================== ===================================
@@ -117,6 +123,42 @@ Assuming you already created one or more products for your general conference ad
* One add-on configuration on your base product that allows users to choose between 0 and 2 products from the category "Workshops"
Option C: Add-on products with variable time slots
""""""""""""""""""""""""""""""""""""""""""""""""""
The above option only works if your conference uses fixed time slots and every workshop uses exactly one time slot. If
your schedule looks like this, it's not going to work great:
+-------------+------------+-----------+
| Time | Room A | Room B |
+=============+============+===========+
| 09:00-11:00 | Talk 1 | Long |
+-------------+------------+ Workshop 1|
| 11:00-13:00 | Talk 2 | |
+-------------+------------+-----------+
| 14:00-16:00 | Long | Talk 3 |
+-------------+ workshop 2 +-----------+
| 16:00-18:00 | | Talk 4 |
+-------------+------------+-----------+
In this case, we recommend that you go to *Settings*, then *Plugins* and activate the plugin **Agenda constraints**.
Then, create a product (without variations) for every single part that should be bookable (talks 1-4 and long workshops
1 and 2) as well as appropriate quotas for each of them.
All of these products should be part of the same category. In your base product (e.g. your conference ticket), you
can then create an add-on product configuration allowing users to add products from this category.
If you edit these products, you will be able to enter the "Start date" and "End date" of the talk or workshop close
to the bottom of the page. If you fill in these values, pretix will automatically ensure no overlapping talks are
booked.
.. note::
This option is currently only available on pretix Hosted. If you are interested in using it with pretix Enterprise,
please contact sales@pretix.eu.
Use case: Discounted packages
-----------------------------

View File

@@ -143,6 +143,11 @@ You can see an example here:
</div>
</noscript>
You can filter events by meta data attributes. You can create those attributes in your order profile and set their values in both event and series date
settings. For example, if you set up a meta data property called "Promoted" that you set to "Yes" on some events, you can pass a filter like this::
<pretix-widget event="https://pretix.eu/demo/series/" style="list" filter="attr[Promoted]=Yes"></pretix-widget>
pretix Button
-------------

View File

@@ -1,71 +0,0 @@
#!/usr/bin/env python
import os
import sys
from datetime import datetime
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pretix.settings")
import django
django.setup()
from pretix.base.models import * # NOQA
from django.utils.timezone import now
if Organizer.objects.exists():
print("There already is data in your DB!")
sys.exit(0)
user = User.objects.get_or_create(
email='admin@localhost',
)[0]
user.set_password('admin')
user.save()
organizer = Organizer.objects.create(
name='BigEvents LLC', slug='bigevents'
)
year = now().year + 1
event = Event.objects.create(
organizer=organizer, name='Demo Conference {}'.format(year),
slug=year, currency='EUR', live=True,
date_from=datetime(year, 9, 4, 17, 0, 0),
date_to=datetime(year, 9, 6, 17, 0, 0),
)
t = Team.objects.get_or_create(
organizer=organizer, name='Admin Team',
all_events=True, can_create_events=True, can_change_teams=True,
can_change_organizer_settings=True, can_change_event_settings=True, can_change_items=True,
can_view_orders=True, can_change_orders=True, can_view_vouchers=True, can_change_vouchers=True
)
t[0].members.add(user)
cat_tickets = ItemCategory.objects.create(
event=event, name='Tickets'
)
cat_merch = ItemCategory.objects.create(
event=event, name='Merchandise'
)
question = Question.objects.create(
event=event, question='Age',
type=Question.TYPE_NUMBER, required=False
)
tr19 = event.tax_rules.create(rate=19)
item_ticket = Item.objects.create(
event=event, category=cat_tickets, name='Ticket',
default_price=23, tax_rule=tr19, admission=True
)
item_ticket.questions.add(question)
item_shirt = Item.objects.create(
event=event, category=cat_merch, name='T-Shirt',
default_price=15, tax_rule=tr19
)
var_s = ItemVariation.objects.create(item=item_shirt, value='S')
var_m = ItemVariation.objects.create(item=item_shirt, value='M')
var_l = ItemVariation.objects.create(item=item_shirt, value='L')
ticket_quota = Quota.objects.create(
event=event, name='Ticket quota', size=400,
)
ticket_quota.items.add(item_ticket)
ticket_shirts = Quota.objects.create(
event=event, name='Shirt quota', size=200,
)
ticket_quota.items.add(item_shirt)
ticket_quota.variations.add(var_s, var_m, var_l)

View File

@@ -127,7 +127,7 @@ class EventSerializer(I18nAwareModelSerializer):
return value
def validate_seat_category_mapping(self, value):
if value and (not self.instance or not self.instance.pk):
if value and value['seat_category_mapping'] and (not self.instance or not self.instance.pk):
raise ValidationError('You cannot specify seat category mappings on event creation.')
item_cache = {i.pk: i for i in self.instance.items.all()}
result = {}

View File

@@ -118,7 +118,8 @@ class ItemSerializer(I18nAwareModelSerializer):
'position', 'picture', 'available_from', 'available_until',
'require_voucher', 'hide_without_voucher', 'allow_cancel', 'require_bundling',
'min_per_order', 'max_per_order', 'checkin_attention', 'has_variations', 'variations',
'addons', 'bundles', 'original_price', 'require_approval', 'generate_tickets')
'addons', 'bundles', 'original_price', 'require_approval', 'generate_tickets',
'show_quota_left')
read_only_fields = ('has_variations', 'picture')
def get_serializer_context(self):
@@ -200,15 +201,25 @@ class InlineQuestionOptionSerializer(I18nAwareModelSerializer):
fields = ('id', 'identifier', 'answer', 'position')
class LegacyDependencyValueField(serializers.CharField):
def to_representation(self, obj):
return obj[0] if obj else None
def to_internal_value(self, data):
return [data] if data else []
class QuestionSerializer(I18nAwareModelSerializer):
options = InlineQuestionOptionSerializer(many=True, required=False)
identifier = serializers.CharField(allow_null=True)
dependency_value = LegacyDependencyValueField(source='dependency_values', required=False, allow_null=True)
class Meta:
model = Question
fields = ('id', 'question', 'type', 'required', 'items', 'options', 'position',
'ask_during_checkin', 'identifier', 'dependency_question', 'dependency_value',
'hidden')
'ask_during_checkin', 'identifier', 'dependency_question', 'dependency_values',
'hidden', 'dependency_value')
def validate_identifier(self, value):
Question._clean_identifier(self.context['event'], value, self.instance)
@@ -262,6 +273,7 @@ class QuestionSerializer(I18nAwareModelSerializer):
def create(self, validated_data):
options_data = validated_data.pop('options') if 'options' in validated_data else []
items = validated_data.pop('items')
question = Question.objects.create(**validated_data)
question.items.set(items)
for opt_data in options_data:
@@ -273,7 +285,7 @@ class QuotaSerializer(I18nAwareModelSerializer):
class Meta:
model = Quota
fields = ('id', 'name', 'size', 'items', 'variations', 'subevent')
fields = ('id', 'name', 'size', 'items', 'variations', 'subevent', 'closed', 'close_when_sold_out')
def validate(self, data):
data = super().validate(data)

View File

@@ -312,7 +312,6 @@ class OrderSerializer(I18nAwareModelSerializer):
# Even though all fields that shouldn't be edited are marked as read_only in the serializer
# (hopefully), we'll be extra careful here and be explicit about the model fields we update.
update_fields = ['comment', 'checkin_attention', 'email', 'locale']
print(validated_data)
if 'invoice_address' in validated_data:
iadata = validated_data.pop('invoice_address')
@@ -598,6 +597,9 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
{'positionid': ["If you set addon_to on any position, you need to specify position IDs manually."]}
for p in data
]
else:
for i, p in enumerate(data):
p['positionid'] = i + 1
if any(errs):
raise ValidationError(errs)

View File

@@ -93,6 +93,7 @@ class CheckinListViewSet(viewsets.ModelViewSet):
)
if not clist.all_products:
pqs = pqs.filter(item__in=clist.limit_products.values_list('id', flat=True))
cqs = cqs.filter(position__item__in=clist.limit_products.values_list('id', flat=True))
ev = clist.subevent or clist.event
response = {
@@ -280,6 +281,7 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
nonce=nonce,
datetime=dt,
questions_supported=self.request.data.get('questions_supported', True),
canceled_supported=self.request.data.get('canceled_supported', False),
user=self.request.user,
auth=self.request.auth,
)

View File

@@ -473,6 +473,19 @@ class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet):
# This costs us a few cycles on save, but avoids thousands of lines in our log.
return
if original_data['closed'] is True and serializer.instance.closed is False:
serializer.instance.log_action(
'pretix.event.quota.opened',
user=self.request.user,
auth=self.request.auth,
)
elif original_data['closed'] is False and serializer.instance.closed is True:
serializer.instance.log_action(
'pretix.event.quota.closed',
user=self.request.user,
auth=self.request.auth,
)
serializer.instance.log_action(
'pretix.event.quota.changed',
user=self.request.user,

View File

@@ -71,6 +71,8 @@ class BaseExporter:
:type form_data: dict
:param form_data: The form data of the export details form
:param output_file: You can optionally accept a parameter that will be given a file handle to write the
output to. In this case, you can return None instead of the file content.
Note: If you use a ``ModelChoiceField`` (or a ``ModelMultipleChoiceField``), the
``form_data`` will not contain the model instance but only it's primary key (or
@@ -111,14 +113,20 @@ class ListExporter(BaseExporter):
def get_filename(self):
return 'export.csv'
def _render_csv(self, form_data, **kwargs):
output = io.StringIO()
writer = csv.writer(output, **kwargs)
for line in self.iterate_list(form_data):
writer.writerow(line)
return self.get_filename() + '.csv', 'text/csv', output.getvalue().encode("utf-8")
def _render_csv(self, form_data, output_file=None, **kwargs):
if output_file:
writer = csv.writer(output_file, **kwargs)
for line in self.iterate_list(form_data):
writer.writerow(line)
return self.get_filename() + '.csv', 'text/csv', None
else:
output = io.StringIO()
writer = csv.writer(output, **kwargs)
for line in self.iterate_list(form_data):
writer.writerow(line)
return self.get_filename() + '.csv', 'text/csv', output.getvalue().encode("utf-8")
def _render_xlsx(self, form_data):
def _render_xlsx(self, form_data, output_file=None):
wb = Workbook()
ws = wb.get_active_sheet()
try:
@@ -129,20 +137,24 @@ class ListExporter(BaseExporter):
for j, val in enumerate(line):
ws.cell(row=i + 1, column=j + 1).value = str(val) if not isinstance(val, KNOWN_TYPES) else val
with tempfile.NamedTemporaryFile(suffix='.xlsx') as f:
wb.save(f.name)
f.seek(0)
return self.get_filename() + '.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', f.read()
if output_file:
wb.save(output_file)
return self.get_filename() + '.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', None
else:
with tempfile.NamedTemporaryFile(suffix='.xlsx') as f:
wb.save(f.name)
f.seek(0)
return self.get_filename() + '.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', f.read()
def render(self, form_data: dict) -> Tuple[str, str, bytes]:
def render(self, form_data: dict, output_file=None) -> Tuple[str, str, bytes]:
if form_data.get('_format') == 'xlsx':
return self._render_xlsx(form_data)
return self._render_xlsx(form_data, output_file=output_file)
elif form_data.get('_format') == 'default':
return self._render_csv(form_data, quoting=csv.QUOTE_NONNUMERIC, delimiter=',')
return self._render_csv(form_data, quoting=csv.QUOTE_NONNUMERIC, delimiter=',', output_file=output_file)
elif form_data.get('_format') == 'csv-excel':
return self._render_csv(form_data, dialect='excel')
return self._render_csv(form_data, dialect='excel', output_file=output_file)
elif form_data.get('_format') == 'semicolon':
return self._render_csv(form_data, dialect='excel', delimiter=';')
return self._render_csv(form_data, dialect='excel', delimiter=';', output_file=output_file)
class MultiSheetListExporter(ListExporter):
@@ -180,14 +192,20 @@ class MultiSheetListExporter(ListExporter):
def iterate_sheet(self, form_data, sheet):
raise NotImplementedError() # noqa
def _render_sheet_csv(self, form_data, sheet, **kwargs):
output = io.StringIO()
writer = csv.writer(output, **kwargs)
for line in self.iterate_sheet(form_data, sheet):
writer.writerow(line)
return self.get_filename() + '.csv', 'text/csv', output.getvalue().encode("utf-8")
def _render_sheet_csv(self, form_data, sheet, output_file=None, **kwargs):
if output_file:
writer = csv.writer(output_file, **kwargs)
for line in self.iterate_sheet(form_data, sheet):
writer.writerow(line)
return self.get_filename() + '.csv', 'text/csv', None
else:
output = io.StringIO()
writer = csv.writer(output, **kwargs)
for line in self.iterate_sheet(form_data, sheet):
writer.writerow(line)
return self.get_filename() + '.csv', 'text/csv', output.getvalue().encode("utf-8")
def _render_xlsx(self, form_data):
def _render_xlsx(self, form_data, output_file=None):
wb = Workbook()
ws = wb.get_active_sheet()
wb.remove(ws)
@@ -197,19 +215,24 @@ class MultiSheetListExporter(ListExporter):
for j, val in enumerate(line):
ws.cell(row=i + 1, column=j + 1).value = str(val) if not isinstance(val, KNOWN_TYPES) else val
with tempfile.NamedTemporaryFile(suffix='.xlsx') as f:
wb.save(f.name)
f.seek(0)
return self.get_filename() + '.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', f.read()
if output_file:
wb.save(output_file)
return self.get_filename() + '.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', None
else:
with tempfile.NamedTemporaryFile(suffix='.xlsx') as f:
wb.save(f.name)
f.seek(0)
return self.get_filename() + '.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', f.read()
def render(self, form_data: dict) -> Tuple[str, str, bytes]:
def render(self, form_data: dict, output_file=None) -> Tuple[str, str, bytes]:
if form_data.get('_format') == 'xlsx':
return self._render_xlsx(form_data)
return self._render_xlsx(form_data, output_file=output_file)
elif ':' in form_data.get('_format'):
sheet, f = form_data.get('_format').split(':')
if f == 'default':
return self._render_sheet_csv(form_data, sheet, quoting=csv.QUOTE_NONNUMERIC, delimiter=',')
return self._render_sheet_csv(form_data, sheet, quoting=csv.QUOTE_NONNUMERIC, delimiter=',',
output_file=output_file)
elif f == 'excel':
return self._render_sheet_csv(form_data, sheet, dialect='excel')
return self._render_sheet_csv(form_data, sheet, dialect='excel', output_file=output_file)
elif f == 'semicolon':
return self._render_sheet_csv(form_data, sheet, dialect='excel', delimiter=';')
return self._render_sheet_csv(form_data, sheet, dialect='excel', delimiter=';', output_file=output_file)

View File

@@ -20,7 +20,7 @@ class InvoiceExporter(BaseExporter):
identifier = 'invoices'
verbose_name = _('All invoices')
def render(self, form_data: dict):
def render(self, form_data: dict, output_file=None):
qs = self.event.invoices.filter(shredded=False)
if form_data.get('payment_provider'):
@@ -47,7 +47,7 @@ class InvoiceExporter(BaseExporter):
with tempfile.TemporaryDirectory() as d:
any = False
with ZipFile(os.path.join(d, 'tmp.zip'), 'w') as zipf:
with ZipFile(output_file or os.path.join(d, 'tmp.zip'), 'w') as zipf:
for i in qs:
try:
if not i.file:
@@ -68,8 +68,11 @@ class InvoiceExporter(BaseExporter):
if not any:
return None
with open(os.path.join(d, 'tmp.zip'), 'rb') as zipf:
return '{}_invoices.zip'.format(self.event.slug), 'application/zip', zipf.read()
if output_file:
return '{}_invoices.zip'.format(self.event.slug), 'application/zip', None
else:
with open(os.path.join(d, 'tmp.zip'), 'rb') as zipf:
return '{}_invoices.zip'.format(self.event.slug), 'application/zip', zipf.read()
@property
def export_form_fields(self):

View File

@@ -1,6 +1,8 @@
import copy
import json
import logging
from decimal import Decimal
from urllib.error import HTTPError
import dateutil.parser
import pytz
@@ -9,6 +11,8 @@ import vat_moss.id
from django import forms
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.db.models import QuerySet
from django.forms import Select
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import get_language, ugettext_lazy as _
@@ -21,9 +25,10 @@ from pretix.base.forms.widgets import (
)
from pretix.base.models import InvoiceAddress, Question, QuestionOption
from pretix.base.models.tax import EU_COUNTRIES
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.base.settings import PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS
from pretix.base.templatetags.rich_text import rich_text
from pretix.control.forms import SplitDateTimeField
from pretix.helpers.escapejson import escapejson_attr
from pretix.helpers.i18n import get_format_without_seconds
from pretix.presale.signals import question_form_fields
@@ -33,14 +38,18 @@ logger = logging.getLogger(__name__)
class NamePartsWidget(forms.MultiWidget):
widget = forms.TextInput
def __init__(self, scheme: dict, field: forms.Field, attrs=None):
def __init__(self, scheme: dict, field: forms.Field, attrs=None, titles: list=None):
widgets = []
self.scheme = scheme
self.field = field
self.titles = titles
for fname, label, size in self.scheme['fields']:
a = copy.copy(attrs) or {}
a['data-fname'] = fname
widgets.append(self.widget(attrs=a))
if fname == 'title' and self.titles:
widgets.append(Select(attrs=a, choices=[('', '')] + [(d, d) for d in self.titles[1]]))
else:
widgets.append(self.widget(attrs=a))
super().__init__(widgets, attrs)
def decompress(self, value):
@@ -99,19 +108,34 @@ class NamePartsFormField(forms.MultiValueField):
'max_length': kwargs.pop('max_length', None),
}
self.scheme_name = kwargs.pop('scheme')
self.titles = kwargs.pop('titles')
self.scheme = PERSON_NAME_SCHEMES.get(self.scheme_name)
if self.titles:
self.scheme_titles = PERSON_NAME_TITLE_GROUPS.get(self.titles)
else:
self.scheme_titles = None
self.one_required = kwargs.get('required', True)
require_all_fields = kwargs.pop('require_all_fields', False)
kwargs['required'] = False
kwargs['widget'] = (kwargs.get('widget') or self.widget)(
scheme=self.scheme, field=self, **kwargs.pop('widget_kwargs', {})
scheme=self.scheme, titles=self.scheme_titles, field=self, **kwargs.pop('widget_kwargs', {})
)
defaults.update(**kwargs)
for fname, label, size in self.scheme['fields']:
defaults['label'] = label
field = forms.CharField(**defaults)
field.part_name = fname
fields.append(field)
if fname == 'title' and self.scheme_titles:
d = dict(defaults)
d.pop('max_length', None)
field = forms.ChoiceField(
**d,
choices=[('', '')] + [(d, d) for d in self.scheme_titles[1]]
)
field.part_name = fname
fields.append(field)
else:
field = forms.CharField(**defaults)
field.part_name = fname
fields.append(field)
super().__init__(
fields=fields, require_all_fields=False, *args, **kwargs
)
@@ -156,6 +180,7 @@ class BaseQuestionsForm(forms.Form):
max_length=255,
required=event.settings.attendee_names_required,
scheme=event.settings.name_scheme,
titles=event.settings.name_scheme_titles,
label=_('Attendee name'),
initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts),
)
@@ -277,7 +302,7 @@ class BaseQuestionsForm(forms.Form):
if q.dependency_question_id:
field.widget.attrs['data-question-dependency'] = q.dependency_question_id
field.widget.attrs['data-question-dependency-value'] = q.dependency_value
field.widget.attrs['data-question-dependency-values'] = escapejson_attr(json.dumps(q.dependency_values))
if q.type != 'M':
field.widget.attrs['required'] = q.required and not self.all_optional
field._required = q.required and not self.all_optional
@@ -298,26 +323,24 @@ class BaseQuestionsForm(forms.Form):
question_cache = {f.question.pk: f.question for f in self.fields.values() if getattr(f, 'question', None)}
def question_is_visible(parentid, qval):
def question_is_visible(parentid, qvals):
parentq = question_cache[parentid]
if parentq.dependency_question_id and not question_is_visible(parentq.dependency_question_id, parentq.dependency_value):
if parentq.dependency_question_id and not question_is_visible(parentq.dependency_question_id, parentq.dependency_values):
return False
if 'question_%d' % parentid not in d:
return False
dval = d.get('question_%d' % parentid)
if qval == 'True':
return dval
elif qval == 'False':
return not dval
elif isinstance(dval, QuestionOption):
return dval.identifier == qval
else:
return qval in [o.identifier for o in dval]
return (
('True' in qvals and dval)
or ('False' in qvals and not dval)
or (isinstance(dval, QuestionOption) and dval.identifier in qvals)
or (isinstance(dval, (list, QuerySet)) and any(qval in [o.identifier for o in dval] for qval in qvals))
)
def question_is_required(q):
return (
q.required and
(not q.dependency_question_id or question_is_visible(q.dependency_question_id, q.dependency_value))
(not q.dependency_question_id or question_is_visible(q.dependency_question_id, q.dependency_values))
)
if not self.all_optional:
@@ -398,6 +421,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
max_length=255,
required=event.settings.invoice_name_required and not self.all_optional,
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),
)
@@ -451,7 +475,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
'your country is currently not available. We will therefore '
'need to charge VAT on your invoice. You can get the tax amount '
'back via the VAT reimbursement process.'))
except vat_moss.errors.WebServiceError:
except (vat_moss.errors.WebServiceError, HTTPError):
logger.exception('VAT ID checking failed for country {}'.format(data.get('country')))
self.instance.vat_id_validated = False
if self.request and self.vat_warning:

View File

@@ -0,0 +1,58 @@
import json
import sys
from django.core.management.base import BaseCommand
from django.utils.timezone import override
from django_scopes import scope
from pretix.base.i18n import language
from pretix.base.models import Event, Organizer
from pretix.base.signals import register_data_exporters
class Command(BaseCommand):
help = "Run an exporter to get data out of pretix"
def add_arguments(self, parser):
parser.add_argument('organizer_slug', nargs=1, type=str)
parser.add_argument('event_slug', nargs=1, type=str)
parser.add_argument('export_provider', nargs=1, type=str)
parser.add_argument('output_file', nargs=1, type=str)
parser.add_argument('--parameters', action='store', type=str, help='JSON-formatted parameters')
def handle(self, *args, **options):
try:
o = Organizer.objects.get(slug=options['organizer_slug'][0])
except Organizer.DoesNotExist:
self.stderr.write(self.style.ERROR('Organizer not found.'))
sys.exit(1)
with scope(organizer=o):
try:
e = o.events.get(slug=options['event_slug'][0])
except Event.DoesNotExist:
self.stderr.write(self.style.ERROR('Event not found.'))
sys.exit(1)
with language(e.settings.locale), override(e.settings.timezone):
responses = register_data_exporters.send(e)
for receiver, response in responses:
ex = response(e)
if ex.identifier == options['export_provider'][0]:
params = json.loads(options.get('parameters') or '{}')
with open(options['output_file'][0], 'wb') as f:
try:
ex.render(form_data=params, output_file=f)
except TypeError:
self.stderr.write(self.style.WARNING(
'Provider does not support direct file writing, need to buffer export in memory.'))
d = ex.render(form_data=params)
if d is None:
self.stderr.write(self.style.ERROR('Empty export.'))
sys.exit(2)
f.write(d[2])
sys.exit(0)
self.stderr.write(self.style.ERROR('Export provider not found.'))
sys.exit(1)

View File

@@ -2,7 +2,7 @@
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 fitlering it from the output.
users from doing that by going really dirty and filtering it from the output.
"""
import sys

View File

@@ -17,7 +17,7 @@ class Command(BaseCommand):
flags = parser.parse_known_args(sys.argv[2:])[1]
if "--override" in flags:
with scopes_disabled():
return call_command("shell", *args, **options)
return call_command("shell_plus", *args, **options)
lookups = {}
for flag in flags:
@@ -36,4 +36,4 @@ class Command(BaseCommand):
for app_name, app_value in lookups.items()
}
with scope(**scope_options):
return call_command("shell", *args, **options)
return call_command("shell_plus", *args, **options)

View File

@@ -0,0 +1,18 @@
# Generated by Django 2.2.1 on 2019-07-10 13:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0125_voucher_show_hidden_items'),
]
operations = [
migrations.AddField(
model_name='item',
name='show_quota_left',
field=models.NullBooleanField(),
),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 2.2.1 on 2019-07-11 07:05
from django.db import migrations
import pretix.base.models.fields
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0126_item_show_quota_left'),
]
operations = [
migrations.RenameField(
model_name='question',
old_name='dependency_value',
new_name='dependency_values',
),
migrations.AlterField(
model_name='question',
name='dependency_values',
field=pretix.base.models.fields.MultiStringField(default=['']),
),
]

View File

@@ -0,0 +1,26 @@
# Generated by Django 2.2.1 on 2019-07-15 15:10
import django.db.models.deletion
from django.db import migrations, models
import pretix.base.models.fields
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0127_auto_20190711_0705'),
]
operations = [
migrations.AddField(
model_name='quota',
name='close_when_sold_out',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='quota',
name='closed',
field=models.BooleanField(default=False),
),
]

View File

@@ -86,7 +86,7 @@ class LoggingMixin:
if (sensitivekey in k) and v:
data[k] = "********"
logentry.data = json.dumps(data, cls=CustomJSONEncoder)
logentry.data = json.dumps(data, cls=CustomJSONEncoder, sort_keys=True)
elif data:
raise TypeError("You should only supply dictionaries as log data.")
if save:

View File

@@ -100,14 +100,14 @@ class EventMixin:
"DATETIME_FORMAT" if self.settings.show_times else "DATE_FORMAT"
)
def get_date_range_display(self, tz=None) -> str:
def get_date_range_display(self, tz=None, force_show_end=False) -> str:
"""
Returns a formatted string containing the start date and the end date
of the event with respect to the current locale and to the ``show_times`` and
``show_date_to`` settings.
"""
tz = tz or self.timezone
if not self.settings.show_date_to or not self.date_to:
if (not self.settings.show_date_to and not force_show_end) or not self.date_to:
return _date(self.date_from.astimezone(tz), "DATE_FORMAT")
return daterange(self.date_from.astimezone(tz), self.date_to.astimezone(tz))
@@ -518,6 +518,11 @@ class Event(EventMixin, LoggedModel):
vars = list(q.variations.all())
q.pk = None
q.event = self
q.cached_availability_state = None
q.cached_availability_number = None
q.cached_availability_paid_orders = None
q.cached_availability_time = None
q.closed = False
q.save()
for i in items:
if i.pk in item_map:
@@ -710,8 +715,12 @@ class Event(EventMixin, LoggedModel):
@property
def meta_data(self):
data = {p.name: p.default for p in self.organizer.meta_properties.all()}
data.update({v.property.name: v.value for v in self.meta_values.select_related('property').all()})
return data
if hasattr(self, 'meta_values_cached'):
data.update({v.property.name: v.value for v in self.meta_values_cached})
else:
data.update({v.property.name: v.value for v in self.meta_values.select_related('property').all()})
return OrderedDict((k, v) for k, v in sorted(data.items(), key=lambda k: k[0]))
@property
def has_payment_provider(self):

View File

@@ -22,6 +22,7 @@ from i18nfield.fields import I18nCharField, I18nTextField
from pretix.base.models import fields
from pretix.base.models.base import LoggedModel
from pretix.base.models.fields import MultiStringField
from pretix.base.models.tax import TaxedPrice
from pretix.base.signals import quota_availability
@@ -168,15 +169,18 @@ def filter_available(qs, channel='web', voucher=None, allow_addons=False):
)
if not allow_addons:
q &= Q(Q(category__isnull=True) | Q(category__is_addon=False))
qs = qs.filter(q)
vouchq = Q(hide_without_voucher=False)
if voucher and voucher.show_hidden_items:
if voucher:
if voucher.item_id:
vouchq = Q(pk=voucher.item_id)
q &= Q(pk=voucher.item_id)
elif voucher.quota_id:
vouchq = Q(quotas__in=[voucher.quota_id])
return qs.filter(vouchq)
q &= Q(quotas__in=[voucher.quota_id])
else:
return qs.none()
if not voucher or not voucher.show_hidden_items:
q &= Q(hide_without_voucher=False)
return qs.filter(q)
class ItemQuerySet(models.QuerySet):
@@ -307,6 +311,11 @@ class Item(LoggedModel):
verbose_name=_("Generate tickets"),
blank=True, null=True,
)
show_quota_left = models.NullBooleanField(
verbose_name=_("Show number of tickets left"),
help_text=_("Publicly show how many tickets are still available."),
blank=True, null=True,
)
position = models.IntegerField(
default=0
)
@@ -405,10 +414,17 @@ class Item(LoggedModel):
self.event.cache.clear()
def delete(self, *args, **kwargs):
self.vouchers.update(item=None, variation=None, quota=None)
super().delete(*args, **kwargs)
if self.event:
self.event.cache.clear()
@property
def do_show_quota_left(self):
if self.show_quota_left is None:
return self.event.settings.show_quota_left
return self.show_quota_left
def tax(self, price=None, base_price_is='auto', currency=None, include_bundled=False):
price = price if price is not None else self.default_price
@@ -643,6 +659,7 @@ class ItemVariation(models.Model):
return t
def delete(self, *args, **kwargs):
self.vouchers.update(item=None, variation=None, quota=None)
super().delete(*args, **kwargs)
if self.item:
self.item.event.cache.clear()
@@ -918,8 +935,8 @@ class Question(LoggedModel):
:type identifier: str
:param dependency_question: This question will only show up if the referenced question is set to `dependency_value`.
:type dependency_question: Question
:param dependency_value: The value that `dependency_question` needs to be set to for this question to be applicable.
:type dependency_value: str
:param dependency_values: The values that `dependency_question` needs to be set to for this question to be applicable.
:type dependency_values: list[str]
"""
TYPE_NUMBER = "N"
TYPE_STRING = "S"
@@ -999,7 +1016,7 @@ class Question(LoggedModel):
dependency_question = models.ForeignKey(
'Question', null=True, blank=True, on_delete=models.SET_NULL, related_name='dependent_questions'
)
dependency_value = models.TextField(null=True, blank=True)
dependency_values = MultiStringField(default=[])
objects = ScopedManager(organizer='event__organizer')
@@ -1252,6 +1269,15 @@ class Quota(LoggedModel):
cached_availability_paid_orders = models.PositiveIntegerField(null=True, blank=True)
cached_availability_time = models.DateTimeField(null=True, blank=True)
close_when_sold_out = models.BooleanField(
verbose_name=_('Close this quota permanently once it is sold out'),
help_text=_('If you enable this, when the quota is sold out once, no more tickets will be sold, '
'even if tickets become available again through cancellations or expiring orders. Of course, '
'you can always re-open it manually.'),
default=False
)
closed = models.BooleanField(default=False)
objects = ScopedManager(organizer='event__organizer')
class Meta:
@@ -1263,6 +1289,7 @@ class Quota(LoggedModel):
return self.name
def delete(self, *args, **kwargs):
self.vouchers.update(item=None, variation=None, quota=None)
super().delete(*args, **kwargs)
if self.event:
self.event.cache.clear()
@@ -1316,6 +1343,11 @@ class Quota(LoggedModel):
count_waitinglist=count_waitinglist):
res = resp
if res[0] <= Quota.AVAILABILITY_ORDERED and self.close_when_sold_out and not self.closed:
self.closed = True
self.save(update_fields=['closed'])
self.log_action('pretix.event.quota.closed')
self.event.cache.delete('item_quota_cache')
rewrite_cache = count_waitinglist and (
not self.cache_is_hot(now_dt) or res[0] > self.cached_availability_state
@@ -1340,8 +1372,11 @@ class Quota(LoggedModel):
_cache['_count_waitinglist'] = count_waitinglist
return res
def _availability(self, now_dt: datetime=None, count_waitinglist=True):
def _availability(self, now_dt: datetime=None, count_waitinglist=True, ignore_closed=False):
now_dt = now_dt or now()
if self.closed and not ignore_closed:
return Quota.AVAILABILITY_ORDERED, 0
size_left = self.size
if size_left is None:
return Quota.AVAILABILITY_OK, None

View File

@@ -870,6 +870,10 @@ class QuestionAnswer(models.Model):
return url
return ""
@property
def is_image(self):
return any(self.file.name.endswith(e) for e in ('.jpg', '.png', '.gif', '.tiff', '.bmp', '.jpeg'))
@property
def file_name(self):
return self.file.name.split('.', 1)[-1]
@@ -1038,18 +1042,17 @@ class AbstractPosition(models.Model):
q.pk: q for q in questions
}
def question_is_visible(parentid, qval):
def question_is_visible(parentid, qvals):
parentq = question_cache[parentid]
if parentq.dependency_question_id and not question_is_visible(parentq.dependency_question_id, parentq.dependency_value):
if parentq.dependency_question_id and not question_is_visible(parentq.dependency_question_id, parentq.dependency_values):
return False
if parentid not in self.answ:
return False
if qval == 'True':
return self.answ[parentid].answer == 'True'
elif qval == 'False':
return self.answ[parentid].answer == 'False'
else:
return qval in [o.identifier for o in self.answ[parentid].options.all()]
return (
('True' in qvals and self.answ[parentid].answer == 'True')
or ('False' in qvals and self.answ[parentid].answer == 'False')
or (any(qval in [o.identifier for o in self.answ[parentid].options.all()] for qval in qvals))
)
self.questions = []
for q in questions:
@@ -1058,7 +1061,7 @@ class AbstractPosition(models.Model):
q.answer.question = q # cache object
else:
q.answer = ""
if not q.dependency_question_id or question_is_visible(q.dependency_question_id, q.dependency_value):
if not q.dependency_question_id or question_is_visible(q.dependency_question_id, q.dependency_values):
self.questions.append(q)
@property
@@ -1185,7 +1188,7 @@ class OrderPayment(models.Model):
@info_data.setter
def info_data(self, d):
self.info = json.dumps(d)
self.info = json.dumps(d, sort_keys=True)
@cached_property
def payment_provider(self):
@@ -1543,7 +1546,7 @@ class OrderRefund(models.Model):
@info_data.setter
def info_data(self, d):
self.info = json.dumps(d)
self.info = json.dumps(d, sort_keys=True)
@cached_property
def payment_provider(self):

View File

@@ -142,22 +142,26 @@ class Voucher(LoggedModel):
item = models.ForeignKey(
Item, related_name='vouchers',
verbose_name=_("Product"),
null=True, blank=True, on_delete=models.CASCADE,
null=True, blank=True,
on_delete=models.PROTECT, # We use a fake version of SET_NULL in Item.delete()
help_text=_(
"This product is added to the user's cart if the voucher is redeemed."
)
)
variation = models.ForeignKey(
ItemVariation, related_name='vouchers',
null=True, blank=True, on_delete=models.CASCADE,
null=True, blank=True,
on_delete=models.PROTECT, # We use a fake version of SET_NULL in ItemVariation.delete() to avoid the semantic change
# that would happen if we just set variation to None
verbose_name=_("Product variation"),
help_text=_(
"This variation of the product select above is being used."
)
)
quota = models.ForeignKey(
Quota, related_name='quota',
null=True, blank=True, on_delete=models.CASCADE,
Quota, related_name='vouchers',
null=True, blank=True,
on_delete=models.PROTECT, # We use a fake version of SET_NULL in Quota.delete()
verbose_name=_("Quota"),
help_text=_(
"If enabled, the voucher is valid for any product affected by this quota."
@@ -408,4 +412,7 @@ class Voucher(LoggedModel):
kwargs['subevent'] = self.subevent
if self.quota_id:
return SeatCategoryMapping.objects.filter(product__quotas__pk=self.quota_id, **kwargs).exists()
return self.item.seat_category_mappings.filter(**kwargs).exists()
elif self.item_id:
return self.item.seat_category_mappings.filter(**kwargs).exists()
else:
return False

View File

@@ -249,9 +249,7 @@ class BasePaymentProvider:
('_fee_percent',
forms.DecimalField(
label=_('Additional fee'),
help_text=_('Percentage of the order total. Note that this percentage will currently only '
'be calculated on the summed price of sold tickets, not on other fees like e.g. shipping '
'fees, if there are any.'),
help_text=_('Percentage of the order total.'),
localize=True,
required=False,
)),
@@ -298,11 +296,12 @@ class BasePaymentProvider:
"""
return ""
def render_invoice_text(self, order: Order) -> str:
def render_invoice_text(self, order: Order, payment: OrderPayment) -> str:
"""
This is called when an invoice for an order with this payment provider is generated.
The default implementation returns the content of the _invoice_text configuration
variable (an I18nString), or an empty string if unconfigured.
variable (an I18nString), or an empty string if unconfigured. For paid orders, the
default implementation always renders a string stating that the invoice is already paid.
"""
if order.status == Order.STATUS_PAID:
return pgettext_lazy('invoice', 'The payment for this invoice has already been received.')
@@ -547,13 +546,14 @@ class BasePaymentProvider:
"""
return None
def order_pending_mail_render(self, order: Order) -> str:
def order_pending_mail_render(self, order: Order, payment: OrderPayment) -> str:
"""
After the user has submitted their order, they will receive a confirmation
email. You can return a string from this method if you want to add additional
information to this email.
:param order: The order object
:param payment: The payment object
"""
return ""

View File

@@ -114,7 +114,7 @@ DEFAULT_VARIABLES = OrderedDict((
("event_date_range", {
"label": _("Event date range"),
"editor_sample": _("May 31st June 4th, 2017"),
"evaluate": lambda op, order, ev: ev.get_date_range_display()
"evaluate": lambda op, order, ev: ev.get_date_range_display(force_show_end=True)
}),
("event_begin", {
"label": _("Event begin date and time"),

View File

@@ -26,6 +26,7 @@ from pretix.base.services.locking import LockTimeoutException, NoLockManager
from pretix.base.services.pricing import get_price
from pretix.base.services.tasks import ProfiledEventTask
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.base.signals import validate_cart_addons
from pretix.base.templatetags.rich_text import rich_text
from pretix.celery_app import app
from pretix.presale.signals import (
@@ -225,7 +226,10 @@ class CartManager:
def _check_item_constraints(self, op):
if isinstance(op, self.AddOperation) or isinstance(op, self.ExtendOperation):
if (op.item.require_voucher or op.item.hide_without_voucher) and (op.voucher is None or not op.voucher.show_hidden_items):
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):
raise CartError(error_messages['voucher_required'])
if not op.item.is_available() or (op.variation and not op.variation.active):
@@ -640,6 +644,15 @@ class CartManager:
'cat': str(iao.addon_category.name),
}
)
validate_cart_addons.send(
sender=self.event,
addons={
(self._items_cache[s[0]], self._variations_cache[s[1]] if s[1] else None)
for s in selected
},
base_position=cp,
iao=iao
)
# Detect removed add-ons and create RemoveOperations
for cp, al in current_addons.items():
@@ -935,6 +948,12 @@ def update_tax_rates(event: Event, cart_id: str, invoice_address: InvoiceAddress
def get_fees(event, request, total, invoice_address, provider):
fees = []
for recv, resp in fee_calculation_for_cart.send(sender=event, request=request, invoice_address=invoice_address,
total=total):
if resp:
fees += resp
total = total + sum(f.value for f in fees)
if provider and total != 0:
provider = event.get_payment_providers().get(provider)
if provider:
@@ -960,10 +979,6 @@ def get_fees(event, request, total, invoice_address, provider):
tax_rule=payment_fee_tax_rule
))
for recv, resp in fee_calculation_for_cart.send(sender=event, request=request, invoice_address=invoice_address,
total=total):
fees += resp
return fees

View File

@@ -60,7 +60,7 @@ def _save_answers(op, answers, given_answers):
@transaction.atomic
def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict, force=False,
ignore_unpaid=False, nonce=None, datetime=None, questions_supported=True,
user=None, auth=None):
user=None, auth=None, canceled_supported=False):
"""
Create a checkin for this particular order position and check-in list. Fails with CheckInError if the check in is
not valid at this time.
@@ -90,10 +90,10 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
'answers'
).get(pk=op.pk)
if op.canceled:
if op.canceled or op.order.status not in (Order.STATUS_PAID, Order.STATUS_PENDING):
raise CheckInError(
_('This order position has been canceled.'),
'unpaid'
'canceled' if canceled_supported else 'unpaid'
)
answers = {a.question: a for a in op.answers.all()}

View File

@@ -1,3 +1,4 @@
import inspect
import json
import logging
import urllib.error
@@ -53,7 +54,10 @@ def build_invoice(invoice: Invoice) -> Invoice:
additional = invoice.event.settings.get('invoice_additional_text', as_type=LazyI18nString)
footer = invoice.event.settings.get('invoice_footer_text', as_type=LazyI18nString)
if open_payment and open_payment.payment_provider:
payment = open_payment.payment_provider.render_invoice_text(invoice.order)
if 'payment' in inspect.signature(open_payment.payment_provider.render_invoice_text).parameters:
payment = open_payment.payment_provider.render_invoice_text(invoice.order, open_payment)
else:
payment = open_payment.payment_provider.render_invoice_text(invoice.order)
elif invoice.order.status == Order.STATUS_PAID:
payment = pgettext('invoice', 'The payment for this invoice has already been received.')
else:

View File

@@ -1,3 +1,4 @@
import inspect
import logging
import smtplib
import warnings
@@ -177,9 +178,9 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
body_plain += "\r\n"
try:
try:
if 'position' in inspect.signature(renderer.render).parameters:
body_html = renderer.render(content_plain, signature, str(subject), order, position)
except TypeError:
else:
# Backwards compatibility
warnings.warn('E-mail renderer called without position argument because position argument is not '
'supported.',

View File

@@ -32,7 +32,7 @@ def notify(logentry_id: int):
# All users that have the permission to get the notification
users = logentry.event.get_users_with_permission(
notification_type.required_permission
).filter(notifications_send=True)
).filter(notifications_send=True, is_active=True)
if logentry.user:
users = users.exclude(pk=logentry.user.pk)

View File

@@ -1,3 +1,4 @@
import inspect
import json
import logging
from collections import Counter, namedtuple
@@ -48,7 +49,7 @@ from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.base.signals import (
allow_ticket_download, order_approved, order_canceled, order_changed,
order_denied, order_expired, order_fee_calculation, order_placed,
periodic_task, validate_order,
order_split, periodic_task, validate_order,
)
from pretix.celery_app import app
from pretix.helpers.models import modelcopy
@@ -505,7 +506,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 (cp.voucher is None or not cp.voucher.show_hidden_items or not cp.voucher.applies_to(cp.item.pk, cp.variation.pk)):
if cp.item.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)):
delete(cp)
cp.delete()
err = error_messages['voucher_required']
@@ -591,6 +592,13 @@ def _get_fees(positions: List[CartPosition], payment_provider: BasePaymentProvid
meta_info: dict, event: Event):
fees = []
total = sum([c.price for c in positions])
for recv, resp in order_fee_calculation.send(sender=event, invoice_address=address, total=total,
meta_info=meta_info, positions=positions):
if resp:
fees += resp
total += sum(f.value for f in fees)
if payment_provider:
payment_fee = payment_provider.calculate_fee(total)
else:
@@ -601,9 +609,6 @@ def _get_fees(positions: List[CartPosition], payment_provider: BasePaymentProvid
internal_type=payment_provider.identifier)
fees.append(pf)
for recv, resp in order_fee_calculation.send(sender=event, invoice_address=address, total=total,
meta_info=meta_info, positions=positions):
fees += resp
return fees, pf
@@ -666,7 +671,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
def _order_placed_email(event: Event, order: Order, pprov: BasePaymentProvider, email_template, log_entry: str,
invoice):
invoice, payment: OrderPayment):
try:
invoice_name = order.invoice_address.name
invoice_company = order.invoice_address.company
@@ -675,7 +680,10 @@ def _order_placed_email(event: Event, order: Order, pprov: BasePaymentProvider,
invoice_company = ""
if pprov:
payment_info = str(pprov.order_pending_mail_render(order))
if 'payment' in inspect.signature(pprov.order_pending_mail_render).parameters:
payment_info = str(pprov.order_pending_mail_render(order, payment))
else:
payment_info = str(pprov.order_pending_mail_render(order))
else:
payment_info = None
@@ -821,7 +829,7 @@ def _perform_order(event: Event, payment_provider: str, position_ids: List[str],
email_attendees = event.settings.mail_send_order_placed_attendee
email_attendees_template = event.settings.mail_text_order_placed_attendee
_order_placed_email(event, order, pprov, email_template, log_entry, invoice)
_order_placed_email(event, order, pprov, email_template, log_entry, invoice, payment)
if email_attendees:
for p in order.positions.all():
if p.addon_to_id is None and p.attendee_email and p.attendee_email != order.email:
@@ -975,6 +983,35 @@ def send_download_reminders(sender, **kwargs):
logger.exception('Reminder email could not be sent to attendee')
def notify_user_changed_order(order, user=None, auth=None):
with language(order.locale):
try:
invoice_name = order.invoice_address.name
invoice_company = order.invoice_address.company
except InvoiceAddress.DoesNotExist:
invoice_name = ""
invoice_company = ""
email_template = order.event.settings.mail_text_order_changed
email_context = {
'event': order.event.name,
'url': build_absolute_uri(order.event, 'presale:event.order.open', kwargs={
'order': order.code,
'secret': order.secret,
'hash': order.email_confirm_hash()
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
email_subject = _('Your order has been changed: %(code)s') % {'code': order.code}
try:
order.send_mail(
email_subject, email_template, email_context,
'pretix.event.order.email.order_changed', user, auth=auth
)
except SendMailException:
logger.exception('Order changed email could not be sent')
class OrderChangeManager:
error_messages = {
'product_without_variation': _('You need to select a variation of the product.'),
@@ -1377,6 +1414,14 @@ class OrderChangeManager:
pass
split_order.total = sum([p.price for p in split_positions if not p.canceled])
for fee in self.order.fees.exclude(fee_type=OrderFee.FEE_TYPE_PAYMENT):
new_fee = modelcopy(fee)
new_fee.pk = None
new_fee.order = split_order
split_order.total += new_fee.value
new_fee.save()
if split_order.total != Decimal('0.00') and self.order.status != Order.STATUS_PAID:
pp = self._get_payment_provider()
if pp:
@@ -1392,13 +1437,6 @@ class OrderChangeManager:
fee.delete()
split_order.total += fee.value
for fee in self.order.fees.exclude(fee_type=OrderFee.FEE_TYPE_PAYMENT):
new_fee = modelcopy(fee)
new_fee.pk = None
new_fee.order = split_order
split_order.total += new_fee.value
new_fee.save()
split_order.save()
if split_order.status == Order.STATUS_PAID:
@@ -1419,6 +1457,8 @@ class OrderChangeManager:
if split_order.total != Decimal('0.00') and self.order.invoices.filter(is_cancellation=False).last():
generate_invoice(split_order)
order_split.send(sender=self.order.event, original=self.order, split_order=split_order)
return split_order
@cached_property
@@ -1504,34 +1544,6 @@ class OrderChangeManager:
except InvoiceAddress.DoesNotExist:
return None
def _notify_user(self, order):
with language(order.locale):
try:
invoice_name = order.invoice_address.name
invoice_company = order.invoice_address.company
except InvoiceAddress.DoesNotExist:
invoice_name = ""
invoice_company = ""
email_template = order.event.settings.mail_text_order_changed
email_context = {
'event': order.event.name,
'url': build_absolute_uri(self.order.event, 'presale:event.order.open', kwargs={
'order': order.code,
'secret': order.secret,
'hash': order.email_confirm_hash()
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
email_subject = _('Your order has been changed: %(code)s') % {'code': order.code}
try:
order.send_mail(
email_subject, email_template, email_context,
'pretix.event.order.email.order_changed', self.user, auth=self.auth
)
except SendMailException:
logger.exception('Order changed email could not be sent')
def commit(self, check_quotas=True):
if self._committed:
# an order change can only be committed once
@@ -1562,9 +1574,9 @@ class OrderChangeManager:
self._check_paid_to_free()
if self.notify:
self._notify_user(self.order)
notify_user_changed_order(self.order, self.user, self.auth)
if self.split_order:
self._notify_user(self.split_order)
notify_user_changed_order(self.split_order, self.user, self.auth)
order_changed.send(self.order.event, order=self.order)

View File

@@ -1,12 +1,16 @@
from datetime import date, datetime, time, timedelta
from decimal import Decimal
from typing import Any, Dict, Iterable, List, Tuple
from django.db.models import Case, Count, F, Sum, Value, When
from django.db.models import (
Case, Count, DateTimeField, F, Max, OuterRef, Subquery, Sum, Value, When,
)
from django.utils.timezone import make_aware
from django.utils.translation import ugettext_lazy as _
from pretix.base.models import Event, Item, ItemCategory, Order, OrderPosition
from pretix.base.models.event import SubEvent
from pretix.base.models.orders import OrderFee
from pretix.base.models.orders import OrderFee, OrderPayment
from pretix.base.signals import order_fee_type_name
@@ -71,8 +75,9 @@ def dictsum(*dicts) -> dict:
return res
def order_overview(event: Event, subevent: SubEvent=None) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
Dict[str, Tuple[Decimal, Decimal]]]:
def order_overview(
event: Event, subevent: SubEvent=None, date_filter='', date_from=None, date_until=None
) -> Tuple[List[Tuple[ItemCategory, List[Item]]], Dict[str, Tuple[Decimal, Decimal]]]:
items = event.items.all().select_related(
'category', # for re-grouping
).prefetch_related(
@@ -82,6 +87,38 @@ def order_overview(event: Event, subevent: SubEvent=None) -> Tuple[List[Tuple[It
qs = OrderPosition.all
if subevent:
qs = qs.filter(subevent=subevent)
if date_from and isinstance(date_from, date):
date_from = make_aware(datetime.combine(
date_from,
time(hour=0, minute=0, second=0, microsecond=0)
), event.timezone)
if date_until and isinstance(date_until, date):
date_until = make_aware(datetime.combine(
date_until + timedelta(days=1),
time(hour=0, minute=0, second=0, microsecond=0)
), event.timezone)
if date_filter == 'order_date':
if date_from:
qs = qs.filter(order__datetime__gte=date_from)
if date_until:
qs = qs.filter(order__datetime__lt=date_until)
elif date_filter == 'last_payment_date':
p_date = OrderPayment.objects.filter(
order=OuterRef('order'),
state__in=[OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_REFUNDED],
payment_date__isnull=False
).values('order').annotate(
m=Max('payment_date')
).values('m').order_by()
qs = qs.annotate(payment_date=Subquery(p_date, output_field=DateTimeField()))
if date_from:
qs = qs.filter(payment_date__gte=date_from)
if date_until:
qs = qs.filter(payment_date__lt=date_until)
counters = qs.filter(
order__event=event
).annotate(
@@ -153,14 +190,26 @@ def order_overview(event: Event, subevent: SubEvent=None) -> Tuple[List[Tuple[It
payment_items = []
if not subevent:
counters = OrderFee.all.filter(
qs = OrderFee.all.filter(
order__event=event
).annotate(
status=Case(
When(canceled=True, then=Value('c')),
default=F('order__status')
)
).values(
)
if date_filter == 'order_date':
if date_from:
qs = qs.filter(order__datetime__gte=date_from)
if date_until:
qs = qs.filter(order__datetime__lt=date_until)
elif date_filter == 'last_payment_date':
qs = qs.annotate(payment_date=Subquery(p_date, output_field=DateTimeField()))
if date_from:
qs = qs.filter(payment_date__gte=date_from)
if date_until:
qs = qs.filter(payment_date__lt=date_until)
counters = qs.values(
'fee_type', 'internal_type', 'status'
).annotate(cnt=Count('id'), value=Sum('value'), tax_value=Sum('tax_value')).order_by()

View File

@@ -701,6 +701,23 @@ Your {event} team"""))
'type': str
}
}
PERSON_NAME_TITLE_GROUPS = OrderedDict([
('english_common', (_('Most common English titles'), (
'Mr',
'Ms',
'Mrs',
'Miss',
'Mx',
'Dr',
'Professor',
'Sir'
))),
('german_common', (_('Most common German titles'), (
'Dr.',
'Prof.',
'Prof. Dr.',
)))
])
PERSON_NAME_SCHEMES = OrderedDict([
('given_family', {
'fields': (
@@ -730,6 +747,22 @@ PERSON_NAME_SCHEMES = OrderedDict([
'_scheme': 'title_given_family',
},
}),
('title_given_family', {
'fields': (
('title', pgettext_lazy('person_name', 'Title'), 1),
('given_name', _('Given name'), 2),
('family_name', _('Family name'), 2),
),
'concatenation': lambda d: ' '.join(
str(p) for p in [d.get('title', ''), d.get('given_name', ''), d.get('family_name', '')] if p
),
'sample': {
'title': pgettext_lazy('person_name_sample', 'Dr'),
'given_name': pgettext_lazy('person_name_sample', 'John'),
'family_name': pgettext_lazy('person_name_sample', 'Doe'),
'_scheme': 'title_given_family',
},
}),
('given_middle_family', {
'fields': (
('given_name', _('First name'), 2),

View File

@@ -265,6 +265,21 @@ appropriate exception message.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
validate_cart_addons = EventPluginSignal(
providing_args=["addons", "base_position", "iao"]
)
"""
This signal is sent when a user tries to select a combination of addons. In contrast to
``validate_cart``, this is executed before the cart is actually modified. You are passed
an argument ``addons`` containing a set of ``(item, variation or None)`` tuples as well
as the ``ItemAddOn`` object as the argument ``iao`` and the base cart position as
``base_position``.
The response of receivers will be ignored, but you can raise a CartError with an
appropriate exception message.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_placed = EventPluginSignal(
providing_args=["order"]
)
@@ -541,3 +556,11 @@ into account.
system really bad.** Also, keep in mind that your response is subject to caching and out-of-date
quotas might be used for display (not for actual order processing).
"""
order_split = EventPluginSignal(
providing_args=["original", "split_order"]
)
"""
This signal is sent out when an order is split into two orders and allows you to copy related models
to the new order. You will be passed the old order as ``original`` and the new order as ``split_order``.
"""

View File

@@ -1,3 +1,5 @@
import json
from django import template
from django.template.defaultfilters import stringfilter
@@ -11,3 +13,9 @@ register = template.Library()
def escapejs_filter(value):
"""Hex encodes characters for use in a application/json type script."""
return escapejson(value)
@register.filter("escapejson_dumps")
def escapejs_dumps_filter(value):
"""Hex encodes characters for use in a application/json type script."""
return escapejson(json.dumps(value))

View File

@@ -24,7 +24,7 @@ from pretix.base.forms import I18nModelForm, PlaceholderValidator, SettingsForm
from pretix.base.models import Event, Organizer, TaxRule
from pretix.base.models.event import EventMetaValue, SubEvent
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.base.settings import PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS
from pretix.control.forms import (
ExtFileField, FontSelect, MultipleLanguagesWidget, SingleLanguageWidget,
SlugWidget, SplitDateTimeField, SplitDateTimePickerWidget,
@@ -383,6 +383,12 @@ class EventSettingsForm(SettingsForm):
"orders might lead to unexpected behaviour when sorting or changing names."),
required=True,
)
name_scheme_titles = forms.ChoiceField(
label=_("Allowed titles"),
help_text=_("If the naming scheme you defined above allows users to input a title, you can use this to "
"restrict the set of selectable titles."),
required=False,
)
attendee_emails_asked = forms.BooleanField(
label=_("Ask for email addresses per ticket"),
help_text=_("Normally, pretix asks for one email address per order and the order confirmation will be sent "
@@ -466,6 +472,13 @@ class EventSettingsForm(SettingsForm):
))
for k, v in PERSON_NAME_SCHEMES.items()
)
self.fields['name_scheme_titles'].choices = [('', _('Free text input'))] + [
(k, '{scheme}: {samples}'.format(
scheme=v[0],
samples=', '.join(v[1])
))
for k, v in PERSON_NAME_TITLE_GROUPS.items()
]
class CancelSettingsForm(SettingsForm):
@@ -1194,6 +1207,10 @@ class DisplaySettingsForm(SettingsForm):
label=_("Show variations of a product expanded by default"),
required=False
)
hide_sold_out = forms.BooleanField(
label=_("Hide all products that are sold out"),
required=False
)
frontpage_subevent_ordering = forms.ChoiceField(
label=pgettext('subevent', 'Date ordering'),
choices=[

View File

@@ -940,3 +940,51 @@ class RefundFilterForm(FilterForm):
OrderRefund.REFUND_STATE_EXTERNAL])
return qs
class OverviewFilterForm(FilterForm):
subevent = forms.ModelChoiceField(
label=pgettext_lazy('subevent', 'Date'),
queryset=SubEvent.objects.none(),
required=False,
empty_label=pgettext_lazy('subevent', 'All dates')
)
date_axis = forms.ChoiceField(
label=_('Date filter'),
choices=(
('', _('Filter by…')),
('order_date', _('Order date')),
('last_payment_date', _('Date of last successful payment')),
),
required=False,
)
date_from = forms.DateField(
label=_('Date from'),
required=False,
widget=DatePickerWidget,
)
date_until = forms.DateField(
label=_('Date until'),
required=False,
widget=DatePickerWidget,
)
def __init__(self, *args, **kwargs):
self.event = kwargs.pop('event')
super().__init__(*args, **kwargs)
if self.event.has_subevents:
self.fields['subevent'].queryset = self.event.subevents.all()
self.fields['subevent'].widget = Select2(
attrs={
'data-model-select2': 'event',
'data-select2-url': reverse('control:event.subevents.select2', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
}),
'data-placeholder': pgettext_lazy('subevent', 'All dates')
}
)
self.fields['subevent'].widget.choices = self.fields['subevent'].choices
elif 'subevent':
del self.fields['subevent']

View File

@@ -55,8 +55,13 @@ class QuestionForm(I18nModelForm):
pk=self.instance.pk
)
self.fields['identifier'].required = False
self.fields['dependency_values'].required = False
self.fields['help_text'].widget.attrs['rows'] = 3
def clean_dependency_values(self):
val = self.data.getlist('dependency_values')
return val
def clean_dependency_question(self):
dep = val = self.cleaned_data.get('dependency_question')
if dep:
@@ -70,8 +75,8 @@ class QuestionForm(I18nModelForm):
def clean(self):
d = super().clean()
if d.get('dependency_question') and not d.get('dependency_value'):
raise ValidationError({'dependency_value': [_('This field is required')]})
if d.get('dependency_question') and not d.get('dependency_values'):
raise ValidationError({'dependency_values': [_('This field is required')]})
if d.get('dependency_question') and d.get('ask_during_checkin'):
raise ValidationError(_('Dependencies between questions are not supported during check-in.'))
return d
@@ -89,13 +94,13 @@ class QuestionForm(I18nModelForm):
'identifier',
'items',
'dependency_question',
'dependency_value'
'dependency_values'
]
widgets = {
'items': forms.CheckboxSelectMultiple(
attrs={'class': 'scrolling-multiple-choice'}
),
'dependency_value': forms.Select,
'dependency_values': forms.SelectMultiple,
}
field_classes = {
'items': SafeModelMultipleChoiceField,
@@ -164,7 +169,8 @@ class QuotaForm(I18nModelForm):
fields = [
'name',
'size',
'subevent'
'subevent',
'close_when_sold_out'
]
field_classes = {
'subevent': SafeModelChoiceField,
@@ -356,6 +362,16 @@ class ItemCreateForm(I18nModelForm):
]
class ShowQuotaNullBooleanSelect(forms.NullBooleanSelect):
def __init__(self, attrs=None):
choices = (
('1', _('(Event default)')),
('2', _('Yes')),
('3', _('No')),
)
super(forms.NullBooleanSelect, self).__init__(attrs, choices)
class TicketNullBooleanSelect(forms.NullBooleanSelect):
def __init__(self, attrs=None):
choices = (
@@ -415,6 +431,7 @@ class ItemUpdateForm(I18nModelForm):
'generate_tickets',
'original_price',
'require_bundling',
'show_quota_left'
]
field_classes = {
'available_from': SplitDateTimeField,
@@ -423,7 +440,8 @@ class ItemUpdateForm(I18nModelForm):
widgets = {
'available_from': SplitDateTimePickerWidget(),
'available_until': SplitDateTimePickerWidget(attrs={'data-date-after': '#id_available_from_0'}),
'generate_tickets': TicketNullBooleanSelect()
'generate_tickets': TicketNullBooleanSelect(),
'show_quota_left': ShowQuotaNullBooleanSelect()
}

View File

@@ -173,6 +173,13 @@ class DeviceForm(forms.ModelForm):
super().__init__(*args, **kwargs)
self.fields['limit_events'].queryset = organizer.events.all()
def clean(self):
d = super().clean()
if not d['all_events'] and not d['limit_events']:
raise ValidationError(_('Your device will not have access to anything, please select some events.'))
return d
class Meta:
model = Device
fields = ['name', 'all_events', 'limit_events']

View File

@@ -148,14 +148,16 @@ class VoucherForm(I18nModelForm):
data, self.instance.event,
self.instance.quota, self.instance.item, self.instance.variation
)
if self.instance.quota:
if all(i.hide_without_voucher for i in self.instance.quota.items.all()):
raise ValidationError({
'itemvar': [
_('The quota you selected only contains hidden products. Hidden products can currently only be '
'shown by using vouchers that directly apply to the product, not via a quota.')
]
})
if not self.instance.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)
):
raise ValidationError({
'show_hidden_items': [
_('The voucher only matches hidden products but you have not selected that it should show '
'them.')
]
})
Voucher.clean_subevent(
data, self.instance.event
)

View File

@@ -263,6 +263,8 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
'pretix.event.quota.added': _('The quota has been added.'),
'pretix.event.quota.deleted': _('The quota has been deleted.'),
'pretix.event.quota.changed': _('The quota has been changed.'),
'pretix.event.quota.closed': _('The quota has closed.'),
'pretix.event.quota.opened': _('The quota has been re-opened.'),
'pretix.event.category.added': _('The category has been added.'),
'pretix.event.category.deleted': _('The category has been deleted.'),
'pretix.event.category.changed': _('The category has been changed.'),

View File

@@ -18,6 +18,7 @@
{% bootstrap_field form.presale_has_ended_text layout="control" %}
{% bootstrap_field form.voucher_explanation_text layout="control" %}
{% bootstrap_field form.show_variations_expanded layout="control" %}
{% bootstrap_field form.hide_sold_out layout="control" %}
{% bootstrap_field form.meta_noindex layout="control" %}
{% if form.frontpage_subevent_ordering %}
{% bootstrap_field form.frontpage_subevent_ordering layout="control" %}

View File

@@ -65,6 +65,7 @@
{% bootstrap_field sform.attendee_names_asked layout="control" %}
{% bootstrap_field sform.attendee_names_required layout="control" %}
{% bootstrap_field sform.name_scheme layout="control" %}
{% bootstrap_field sform.name_scheme_titles layout="control" %}
{% bootstrap_field sform.order_email_asked_twice layout="control" %}
{% bootstrap_field sform.attendee_emails_asked layout="control" %}
{% bootstrap_field sform.attendee_emails_required layout="control" %}

View File

@@ -87,6 +87,11 @@
<td class="event-name-col">
<strong><a href="{% url "control:event.index" organizer=e.organizer.slug event=e.slug %}">{{ e.name }}</a></strong>
<br><small>{{ e.slug }}</small>
{% for k, v in e.meta_data.items %}
{% if v %}
<span class="text-muted">&middot; {{ k }}: {{ v }}</span>
{% endif %}
{% endfor %}
</td>
{% if not hide_orga %}<td>{{ e.organizer }}</td>{% endif %}
<td class="event-date-col">

View File

@@ -17,6 +17,19 @@
{% csrf_token %}
{% if possible %}
<p>{% blocktrans %}Are you sure you want to delete the product <strong>{{ item }}</strong>?{% endblocktrans %}</p>
{% if vouchers %}
<div class="alert alert-warning">
{% blocktrans trimmed count count=vouchers %}
That will cause {{ count }} voucher to be unusable.
{% plural %}
That will cause {{ count }} voucher to be unusable.
{% endblocktrans %}
<a href="{% url "control:event.vouchers" organizer=request.organizer.slug event=request.event.slug %}?itemvar={{ item.pk }}"
class="btn btn-default">
{% trans "Show affected vouchers" %}
</a>
</div>
{% endif %}
{% else %}
<p>{% blocktrans %}You cannot delete the product <strong>{{ item }}</strong> because it already has been ordered, but you can deactivate it.{% endblocktrans %}</p>
{% endif %}

View File

@@ -45,6 +45,7 @@
{% bootstrap_field form.original_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.require_approval layout="control" %}
{% bootstrap_field form.generate_tickets layout="control" %}
{% bootstrap_field form.show_quota_left layout="control" %}
{% for f in plugin_forms %}
{% bootstrap_form f layout="control" %}
{% endfor %}

View File

@@ -1,5 +1,7 @@
{% load i18n %}
{% if availability.0 == 10 %}
{% if closed %}
<span class="label label-danger">{% trans "Closed" %}</span>
{% elif availability.0 == 10 %}
<span class="label label-warning">{% trans "Sold out (pending orders)" %}</span>
{% elif availability.0 == 100 %}
{% if availability.1 != None %}

View File

@@ -2,6 +2,7 @@
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
{% load escapejson %}
{% block title %}
{% if question %}
{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}
@@ -120,8 +121,8 @@
{% bootstrap_field form.dependency_question layout="inline" form_group_class="inner" %}
</div>
<div class="col-md-5">
<script type="text/plain" id="dependency_value_val">{{ form.instance.dependency_value }}</script>
{% bootstrap_field form.dependency_value layout="inline" form_group_class="inner" %}
<script type="text/plain" id="dependency_value_val">{{ form.instance.dependency_values|escapejson_dumps }}</script>
{% bootstrap_field form.dependency_values layout="inline" form_group_class="inner" %}
</div>
</div>
</fieldset>

View File

@@ -20,6 +20,27 @@
<span class="fa fa-calendar"></span> {{ quota.subevent.name }} {{ quota.subevent.get_date_range_display }}
</p>
{% endif %}
<form action="" method="post">
{% csrf_token %}
{% if quota.closed %}
{% if closed_and_sold_out %}
<div class="alert alert-info">
<button type="submit" class="btn btn-default pull-right" name="disable" value="true">
{% trans "Open quota and disable closing" %}
</button>
{% trans "This quota is sold out and closed. Even if tickets become available e.g. through cancellations, they will not become available again unless you manually re-open the quota on this page." %}
<div class="clearfix"></div>
</div>
{% else %}
<div class="alert alert-warning">
<button type="submit" class="btn btn-primary pull-right" name="reopen" value="true">{% trans "Open quota" %}</button>
{% trans "This quota is closed since it has been sold out before. Tickets are theoretically available, but will not be sold unless you manually re-open the quota." %}
<div class="clearfix"></div>
</div>
{% endif %}
{% endif %}
</form>
<div class="row" id="quota-stats">
<div class="col-md-5 col-xs-12">
<legend>{% trans "Usage overview" %}</legend>
@@ -30,7 +51,6 @@
</div>
<div class="col-md-5 col-xs-12">
<legend>{% trans "Availability calculation" %}</legend>
<div class="row">
<div class="col-xs-9">{% trans "Total quota" %}</div>
<div class="col-xs-3 text-right">

View File

@@ -3,16 +3,35 @@
{% load bootstrap3 %}
{% block title %}{% trans "Delete quota" %}{% endblock %}
{% block inside %}
<h1>{% trans "Delete quota" %}</h1>
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<p>{% blocktrans %}Are you sure you want to delete the quota <strong>{{ quota }}</strong>?{% endblocktrans %}</p>
{% if dependent|length > 0 %}
<p>{% blocktrans %}The following products might be no longer available for sale:{% endblocktrans %}</p>
{% for item in dependent %}
<li><a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.pk %}">{{ item.name }}</a></li>
{% endfor %}
{% endif %}
<h1>{% trans "Delete quota" %}</h1>
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<p>{% blocktrans %}Are you sure you want to delete the quota <strong>{{ quota }}</strong>?{% endblocktrans %}</p>
{% if dependent|length > 0 %}
<div class="alert alert-info">
<p>{% blocktrans %}The following products might be no longer available for sale:{% endblocktrans %}</p>
<ul>
{% for item in dependent %}
<li>
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.pk %}">{{ item.name }}</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if vouchers %}
<div class="alert alert-warning">
{% blocktrans trimmed count count=vouchers %}
That will cause {{ count }} voucher to be unusable.
{% plural %}
That will cause {{ count }} voucher to be unusable.
{% endblocktrans %}
<a href="{% url "control:event.vouchers" organizer=request.organizer.slug event=request.event.slug %}?itemvar=q-{{ quota.pk }}"
class="btn btn-default">
{% trans "Show affected vouchers" %}
</a>
</div>
{% endif %}
<div class="form-group submit-group">
<a href="{% url "control:event.items.quotas" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default btn-cancel">
{% trans "Cancel" %}
@@ -20,6 +39,6 @@
<button type="submit" class="btn btn-danger btn-save">
{% trans "Delete" %}
</button>
</div>
</form>
</div>
</form>
{% endblock %}

View File

@@ -24,6 +24,8 @@
{% if form.subevent %}
{% bootstrap_field form.subevent layout="control" %}
{% endif %}
</fieldset>
<fieldset>
<legend>{% trans "Items" %}</legend>
<p>
{% blocktrans trimmed %}
@@ -35,6 +37,10 @@
</p>
{% bootstrap_field form.itemvars layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Advanced options" %}</legend>
{% bootstrap_field form.close_when_sold_out layout="control" %}
</fieldset>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}

View File

@@ -74,7 +74,7 @@
<td>{{ q.subevent.name }} {{ q.subevent.get_date_range_display }}</td>
{% endif %}
<td>{% if q.size == None %}Unlimited{% else %}{{ q.size }}{% endif %}</td>
<td>{% include "pretixcontrol/items/fragment_quota_availability.html" with availability=q.availability %}</td>
<td>{% include "pretixcontrol/items/fragment_quota_availability.html" with availability=q.availability closed=q.closed %}</td>
<td class="text-right">
<a href="{% url "control:event.items.quotas.edit" organizer=request.event.organizer.slug event=request.event.slug quota=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
<a href="{% url "control:event.items.quotas.delete" organizer=request.event.organizer.slug event=request.event.slug quota=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>

View File

@@ -59,6 +59,24 @@
</details>
{% endfor %}
</div>
<div class="panel panel-default items">
<div class="panel-heading">
<h3 class="panel-title">
{% trans "Other operations" %}
</h3>
</div>
<div class="panel-body">
<div class="form-horizontal">
{% bootstrap_form_errors other_form %}
{% if other_form.custom_error %}
<div class="alert alert-danger">
{{ other_form.custom_error }}
</div>
{% endif %}
{% bootstrap_field other_form.notify layout="control" %}
</div>
</div>
</div>
<div class="form-group submit-group">
<a class="btn btn-default btn-lg"
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">

View File

@@ -332,6 +332,13 @@
title="{% trans "This file has been uploaded by a user and could contain viruses or other malicious content." %}">
{% trans "UNSAFE" %}
</span>
{% if q.answer.is_image %}
<br>
<a href="{{ q.answer.backend_file_url }}?token={% answer_token request q.answer %}" data-lightbox="order"
class="answer-thumb">
<img src="{{ q.answer.backend_file_url }}?token={% answer_token request q.answer %}">
</a>
{% endif %}
{% else %}
{{ q.answer|linebreaksbr }}
{% endif %}

View File

@@ -1,5 +1,6 @@
{% extends "pretixcontrol/event/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load order_overview %}
{% block title %}{% trans "Order overview" %}{% endblock %}
{% block content %}
@@ -12,11 +13,36 @@
</div>
</div>
<h1>{% trans "Order overview" %}</h1>
{% if request.event.has_subevents %}
<form class="form-inline helper-display-inline" action="" method="get">
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" %}
<div class="row filter-form">
<form class="" action="" method="get">
{% if request.event.has_subevents %}
<div class="col-lg-2 col-sm-3 col-xs-6">
{% bootstrap_field filter_form.subevent layout='inline' %}
</div>
<div class="col-lg-2 col-sm-3 col-xs-6">
{% bootstrap_field filter_form.date_axis layout='inline' %}
</div>
{% else %}
<div class="col-lg-4 col-sm-6 col-xs-6">
{% bootstrap_field filter_form.date_axis layout='inline' %}
</div>
{% endif %}
<div class="col-lg-2 col-sm-6 col-xs-6">
{% bootstrap_field filter_form.date_from layout='inline' %}
</div>
<div class="col-lg-2 col-sm-6 col-xs-6">
{% bootstrap_field filter_form.date_until layout='inline' %}
</div>
<div class="col-lg-1 col-lg-offset-3 col-sm-6 col-xs-6">
<button class="btn btn-primary btn-block" type="submit">
<span class="fa fa-filter"></span>
<span class="hidden-md">
{% trans "Filter" %}
</span>
</button>
</div>
</form>
{% endif %}
</div>
{% if subevent_warning %}
<div class="alert alert-info">
{% blocktrans trimmed context "subevent" %}

View File

@@ -7,7 +7,16 @@
<div>
<ol>
<li>{% trans "Open the app that you want to connect and optionally reset it to the original state." %}</li>
<li>
{% trans "Download an app that is compatible with pretix. For example, our check-in app <strong>pretixSCAN</strong> is available on all major platforms." %}<br>
<a href="https://pretix.eu/about/{% if "de" in request.LANGUAGE_CODE %}de{% else %}en{% endif %}/scan"
class="btn btn-default" target="_blank">
{% trans "Download pretixSCAN" %}
</a>
</li>
<li>
{% trans "Open the app that you want to connect and optionally reset it to the original state." %}
</li>
<li>{% trans "Scan the following configuration code:" %}<br><br>
<script type="text/json" data-replace-with-qr>{{ qrdata|safe }}</script><br>
{% trans "If your app/device does not support scanning a QR code, you can also enter the following information:" %}
@@ -17,19 +26,6 @@
</li>
</ol>
</div>
<div class="alert alert-warning">
<strong>
{% blocktrans trimmed %}
Please note that this is a new feature that currently only works for beta-stage software, such as
pretixPOS. pretixdroid 1.x and pretixdesk 0.x are not supported by this feature. Future versions of
pretixdroid and pretixdesk will be supported through this menu.
{% endblocktrans %}
</strong>
<br><br>
{% blocktrans trimmed %}
To set up pretixdroid or pretixdesk, please go to the <strong>Check-in devices</strong> section of an event.
{% endblocktrans %}
</div>
<a href="{% url "control:organizer.devices" organizer=request.organizer.slug %}"
class="btn btn-default"><i class="fa fa-arrow-left"></i>
{% trans "Device overview" %}

View File

@@ -5,23 +5,12 @@
<h1>
{% trans "Connected devices" %}
</h1>
<div class="alert alert-info">
<p>
{% blocktrans trimmed %}
This menu allows you to connect hardware devices such as box office terminals or scanning terminals to
your account.
{% endblocktrans %}
<strong>
{% blocktrans trimmed %}
Please note that this is a new feature that currently only works for beta-stage software, such as
pretixPOS. pretixdroid 1.x and pretixdesk 0.x are not supported by this feature. Future versions of
pretixdroid and pretixdesk will be supported through this menu.
{% endblocktrans %}
</strong>
<br><br>
{% blocktrans trimmed %}
To set up pretixdroid or pretixdesk, please go to the <strong>Check-in devices</strong> section of an event.
{% endblocktrans %}
</div>
</p>
{% if devices|length == 0 %}
<div class="empty-collection">
<p>

View File

@@ -128,7 +128,7 @@
{% if v.variation %}
{{ v.variation }}
{% endif %}
{% else %}
{% elif v.quota %}
{% blocktrans trimmed with quota=v.quota.name %}
Any product in quota "{{ quota }}"
{% endblocktrans %}

View File

@@ -1,6 +1,7 @@
import json
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.core.files import File
from django.db import transaction
from django.db.models import Count, F, Prefetch, Q
@@ -684,6 +685,8 @@ class QuotaView(ChartContainingView, DetailView):
Q(Q(self.object._position_lookup) | Q(quota=self.object)) &
Q(redeemed__lt=F('max_usages'))
).exists()
if self.object.closed:
ctx['closed_and_sold_out'] = self.object._availability(ignore_closed=True)[0] <= Quota.AVAILABILITY_ORDERED
return ctx
@@ -695,6 +698,32 @@ class QuotaView(ChartContainingView, DetailView):
except Quota.DoesNotExist:
raise Http404(_("The requested quota does not exist."))
def post(self, request, *args, **kwargs):
if not request.user.has_event_permission(request.organizer, request.event, 'can_change_items', request):
raise PermissionDenied()
quota = self.get_object()
if 'reopen' in request.POST:
quota.closed = False
quota.save(update_fields=['closed'])
quota.log_action('pretix.event.quota.opened', user=request.user)
messages.success(request, _('The quota has been re-opened.'))
if 'disable' in request.POST:
quota.closed = False
quota.close_when_sold_out = False
quota.save(update_fields=['closed', 'close_when_sold_out'])
quota.log_action('pretix.event.quota.opened', user=request.user)
quota.log_action(
'pretix.event.quota.changed', user=self.request.user, data={
'close_when_sold_out': False
}
)
messages.success(request, _('The quota has been re-opened and will not close again.'))
return redirect(reverse('control:event.items.quotas.show', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'quota': quota.pk
}))
class QuotaUpdate(EventPermissionRequiredMixin, UpdateView):
model = Quota
@@ -771,7 +800,8 @@ class QuotaDelete(EventPermissionRequiredMixin, DeleteView):
def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs)
context['dependent'] = list(self.get_object().items.all())
context['dependent'] = list(self.object.items.all())
context['vouchers'] = self.object.vouchers.count()
return context
@transaction.atomic
@@ -1183,6 +1213,7 @@ class ItemDelete(EventPermissionRequiredMixin, DeleteView):
def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs)
context['possible'] = self.is_allowed()
context['vouchers'] = self.object.vouchers.count()
return context
def is_allowed(self) -> bool:

View File

@@ -17,7 +17,7 @@ from i18nfield.strings import LazyI18nString
from pretix.base.forms import SafeSessionWizardView
from pretix.base.i18n import language
from pretix.base.models import Event, Organizer, Quota, Team
from pretix.base.models import Event, EventMetaValue, Organizer, Quota, Team
from pretix.control.forms.event import (
EventWizardBasicsForm, EventWizardCopyForm, EventWizardFoundationForm,
)
@@ -32,8 +32,13 @@ class EventList(PaginationMixin, ListView):
template_name = 'pretixcontrol/events/index.html'
def get_queryset(self):
qs = self.request.user.get_events_with_any_permission(self.request).select_related('organizer').prefetch_related(
'_settings_objects', 'organizer___settings_objects'
qs = self.request.user.get_events_with_any_permission(self.request).prefetch_related(
'organizer', '_settings_objects', 'organizer___settings_objects', 'organizer__meta_properties',
Prefetch(
'meta_values',
EventMetaValue.objects.select_related('property'),
to_attr='meta_values_cached'
)
).order_by('-date_from')
qs = qs.annotate(

View File

@@ -38,7 +38,6 @@ from pretix.base.models import (
Item, ItemVariation, LogEntry, Order, QuestionAnswer, Quota,
generate_position_secret, generate_secret,
)
from pretix.base.models.event import SubEvent
from pretix.base.models.orders import (
OrderFee, OrderPayment, OrderPosition, OrderRefund,
)
@@ -55,6 +54,7 @@ from pretix.base.services.mail import SendMailException, render_mail
from pretix.base.services.orders import (
OrderChangeManager, OrderError, approve_order, cancel_order, deny_order,
extend_order, mark_order_expired, mark_order_refunded,
notify_user_changed_order,
)
from pretix.base.services.stats import order_overview
from pretix.base.services.tickets import generate
@@ -65,7 +65,9 @@ from pretix.base.templatetags.money import money_filter
from pretix.base.templatetags.rich_text import markdown_compile_email
from pretix.base.views.mixins import OrderQuestionsViewMixin
from pretix.base.views.tasks import AsyncAction
from pretix.control.forms.filter import EventOrderFilterForm, RefundFilterForm
from pretix.control.forms.filter import (
EventOrderFilterForm, OverviewFilterForm, RefundFilterForm,
)
from pretix.control.forms.orders import (
CancelForm, CommentForm, ConfirmPaymentForm, ExporterForm, ExtendForm,
MarkPaidForm, OrderContactForm, OrderLocaleForm, OrderMailForm,
@@ -1116,7 +1118,8 @@ class InvoiceDownload(EventPermissionRequiredMixin, View):
invoice_pdf_task.apply(args=(self.invoice.pk,))
return self.get(request, *args, **kwargs)
resp['Content-Disposition'] = 'attachment; filename="{}.pdf"'.format(self.invoice.number)
resp['Content-Disposition'] = 'inline; filename="{}.pdf"'.format(self.invoice.number)
resp._csp_ignore = True # Some browser's PDF readers do not work with CSP
return resp
@@ -1319,12 +1322,27 @@ class OrderModifyInformation(OrderQuestionsViewMixin, OrderView):
only_user_visible = False
all_optional = True
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['other_form'] = self.other_form
return ctx
@cached_property
def other_form(self):
return OtherOperationsForm(prefix='other', order=self.order,
data=self.request.POST if self.request.method == "POST" else None)
def post(self, request, *args, **kwargs):
failed = not self.save() or not self.invoice_form.is_valid()
failed = not self.save() or not self.invoice_form.is_valid() or not self.other_form.is_valid()
notify = self.other_form.cleaned_data['notify'] if self.other_form.is_valid() else True
if failed:
messages.error(self.request,
_("We had difficulties processing your input. Please review the errors below."))
return self.get(request, *args, **kwargs)
if notify:
notify_user_changed_order(self.order)
if hasattr(self.invoice_form, 'save'):
self.invoice_form.save()
self.order.log_action('pretix.event.order.modified', {
@@ -1577,21 +1595,32 @@ class OverView(EventPermissionRequiredMixin, TemplateView):
template_name = 'pretixcontrol/orders/overview.html'
permission = 'can_view_orders'
@cached_property
def filter_form(self):
return OverviewFilterForm(data=self.request.GET, event=self.request.event)
def get_context_data(self, **kwargs):
ctx = super().get_context_data()
subevent = None
if self.request.GET.get("subevent", "") != "" and self.request.event.has_subevents:
i = self.request.GET.get("subevent", "")
try:
subevent = self.request.event.subevents.get(pk=i)
except SubEvent.DoesNotExist:
pass
ctx['items_by_category'], ctx['total'] = order_overview(self.request.event, subevent=subevent)
ctx['subevent_warning'] = self.request.event.has_subevents and subevent and (
if self.filter_form.is_valid():
ctx['items_by_category'], ctx['total'] = order_overview(
self.request.event,
subevent=self.filter_form.cleaned_data.get('subevent'),
date_filter=self.filter_form.cleaned_data['date_axis'],
date_from=self.filter_form.cleaned_data['date_from'],
date_until=self.filter_form.cleaned_data['date_until'],
)
else:
ctx['items_by_category'], ctx['total'] = order_overview(
self.request.event,
)
ctx['subevent_warning'] = (
self.request.event.has_subevents and
self.filter_form.is_valid() and
self.filter_form.cleaned_data.get('subevent') and
OrderFee.objects.filter(order__event=self.request.event).exclude(value=0).exists()
)
ctx['filter_form'] = self.filter_form
return ctx

View File

@@ -9,8 +9,23 @@ _json_escapes = {
ord('&'): '\\u0026',
}
_json_escapes_attr = {
ord('>'): '\\u003E',
ord('<'): '\\u003C',
ord('&'): '\\u0026',
ord('"'): '&#34;',
ord("'"): '&#39;',
ord("="): '&#61;',
}
@keep_lazy(six.text_type, SafeText)
def escapejson(value):
"""Hex encodes characters for use in a application/json type script."""
return mark_safe(force_text(value).translate(_json_escapes))
@keep_lazy(six.text_type, SafeText)
def escapejson_attr(value):
"""Hex encodes characters for use in a html attributw script."""
return mark_safe(force_text(value).translate(_json_escapes_attr))

View File

@@ -1,3 +1,4 @@
from django.conf import settings
from django.http import StreamingHttpResponse
@@ -9,3 +10,12 @@ class ChunkBasedFileResponse(StreamingHttpResponse):
streaming_content = streaming_content.chunks(self.block_size)
super().__init__(streaming_content, *args, **kwargs)
self['Content-Length'] = filelike.size
def get_client_ip(request):
ip = request.META.get('REMOTE_ADDR')
if settings.TRUST_X_FORWARDED_FOR:
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
return ip

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: 2019-06-25 09:08+0000\n"
"POT-Creation-Date: 2019-07-15 08:31+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -46,61 +46,71 @@ msgstr ""
msgid "Contacting Stripe …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
msgid "Total"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:144
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:175
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:151
msgid "Contacting your bank …"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
#: pretix/static/pretixbase/js/asynctask.js:105
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."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
#: pretix/static/pretixbase/js/asynctask.js:111
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:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixbase/js/asynctask.js:76
#: pretix/static/pretixbase/js/asynctask.js:142
#: pretix/static/pretixbase/js/asynctask.js:147
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:69
#: pretix/static/pretixbase/js/asynctask.js:79
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixbase/js/asynctask.js:125
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixbase/js/asynctask.js:150
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:148
#: pretix/static/pretixbase/js/asynctask.js:171
msgid "We are processing your request …"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:156
#: pretix/static/pretixbase/js/asynctask.js:179
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:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
#: pretix/static/pretixbase/js/asynctask.js:216
#: pretix/static/pretixcontrol/js/ui/main.js:34
msgid "Close message"
msgstr ""
@@ -168,37 +178,37 @@ msgstr ""
msgid "Generating messages …"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:55
#: pretix/static/pretixcontrol/js/ui/main.js:69
msgid "Unknown error."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:217
#: pretix/static/pretixcontrol/js/ui/main.js:231
msgid "Your color has great contrast and is very easy to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:221
#: pretix/static/pretixcontrol/js/ui/main.js:235
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:225
#: pretix/static/pretixcontrol/js/ui/main.js:239
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:305
#: pretix/static/pretixcontrol/js/ui/main.js:319
msgid "All"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:306
#: pretix/static/pretixcontrol/js/ui/main.js:320
msgid "None"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:595
#: pretix/static/pretixcontrol/js/ui/main.js:609
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:652
#: pretix/static/pretixcontrol/js/ui/main.js:666
msgid "Click to close"
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: 2019-06-25 09:08+0000\n"
"POT-Creation-Date: 2019-07-15 08:31+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -45,61 +45,71 @@ msgstr ""
msgid "Contacting Stripe …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
msgid "Total"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:144
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:175
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:151
msgid "Contacting your bank …"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
#: pretix/static/pretixbase/js/asynctask.js:105
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."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
#: pretix/static/pretixbase/js/asynctask.js:111
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:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixbase/js/asynctask.js:76
#: pretix/static/pretixbase/js/asynctask.js:142
#: pretix/static/pretixbase/js/asynctask.js:147
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:69
#: pretix/static/pretixbase/js/asynctask.js:79
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixbase/js/asynctask.js:125
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixbase/js/asynctask.js:150
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:148
#: pretix/static/pretixbase/js/asynctask.js:171
msgid "We are processing your request …"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:156
#: pretix/static/pretixbase/js/asynctask.js:179
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:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
#: pretix/static/pretixbase/js/asynctask.js:216
#: pretix/static/pretixcontrol/js/ui/main.js:34
msgid "Close message"
msgstr ""
@@ -167,37 +177,37 @@ msgstr ""
msgid "Generating messages …"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:55
#: pretix/static/pretixcontrol/js/ui/main.js:69
msgid "Unknown error."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:217
#: pretix/static/pretixcontrol/js/ui/main.js:231
msgid "Your color has great contrast and is very easy to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:221
#: pretix/static/pretixcontrol/js/ui/main.js:235
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:225
#: pretix/static/pretixcontrol/js/ui/main.js:239
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:305
#: pretix/static/pretixcontrol/js/ui/main.js:319
msgid "All"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:306
#: pretix/static/pretixcontrol/js/ui/main.js:320
msgid "None"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:595
#: pretix/static/pretixcontrol/js/ui/main.js:609
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:652
#: pretix/static/pretixcontrol/js/ui/main.js:666
msgid "Click to close"
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: 2019-06-25 09:08+0000\n"
"POT-Creation-Date: 2019-07-15 08:31+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -45,61 +45,71 @@ msgstr ""
msgid "Contacting Stripe …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
msgid "Total"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:144
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:175
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:151
msgid "Contacting your bank …"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
#: pretix/static/pretixbase/js/asynctask.js:105
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."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
#: pretix/static/pretixbase/js/asynctask.js:111
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:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixbase/js/asynctask.js:76
#: pretix/static/pretixbase/js/asynctask.js:142
#: pretix/static/pretixbase/js/asynctask.js:147
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:69
#: pretix/static/pretixbase/js/asynctask.js:79
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixbase/js/asynctask.js:125
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixbase/js/asynctask.js:150
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:148
#: pretix/static/pretixbase/js/asynctask.js:171
msgid "We are processing your request …"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:156
#: pretix/static/pretixbase/js/asynctask.js:179
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:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
#: pretix/static/pretixbase/js/asynctask.js:216
#: pretix/static/pretixcontrol/js/ui/main.js:34
msgid "Close message"
msgstr ""
@@ -167,37 +177,37 @@ msgstr ""
msgid "Generating messages …"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:55
#: pretix/static/pretixcontrol/js/ui/main.js:69
msgid "Unknown error."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:217
#: pretix/static/pretixcontrol/js/ui/main.js:231
msgid "Your color has great contrast and is very easy to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:221
#: pretix/static/pretixcontrol/js/ui/main.js:235
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:225
#: pretix/static/pretixcontrol/js/ui/main.js:239
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:305
#: pretix/static/pretixcontrol/js/ui/main.js:319
msgid "All"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:306
#: pretix/static/pretixcontrol/js/ui/main.js:320
msgid "None"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:595
#: pretix/static/pretixcontrol/js/ui/main.js:609
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:652
#: pretix/static/pretixcontrol/js/ui/main.js:666
msgid "Click to close"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-06-25 09:08+0000\n"
"POT-Creation-Date: 2019-07-15 08:31+0000\n"
"PO-Revision-Date: 2018-04-24 14:22+0000\n"
"Last-Translator: Pernille Thorsen <perth@aarhus.dk>\n"
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -46,12 +46,23 @@ msgstr "Omsætning i alt"
msgid "Contacting Stripe …"
msgstr "Kontakter Stripe …"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
msgid "Total"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:144
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:175
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:151
#, fuzzy
#| msgid "Contacting Stripe …"
msgid "Contacting your bank …"
msgstr "Kontakter Stripe …"
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
#: pretix/static/pretixbase/js/asynctask.js:105
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."
@@ -60,7 +71,7 @@ msgstr ""
"der gå op til et par minutter."
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
#: pretix/static/pretixbase/js/asynctask.js:111
#, fuzzy
#| msgid ""
#| "Your request has been queued on the server and will now be processed. If "
@@ -74,13 +85,14 @@ msgstr ""
"Din forespørgsel er under behandling. Hvis der går mere end to minutter, så "
"kontakt os eller gå tilbage og prøv igen."
#: pretix/static/pretixbase/js/asynctask.js:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixbase/js/asynctask.js:76
#: pretix/static/pretixbase/js/asynctask.js:142
#: pretix/static/pretixbase/js/asynctask.js:147
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr "Der er sket en fejl ({code})."
#: pretix/static/pretixbase/js/asynctask.js:69
#: pretix/static/pretixbase/js/asynctask.js:79
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
@@ -88,12 +100,12 @@ msgstr ""
"Vi kan ikke komme i kontakt med serveren, men prøver igen. Seneste fejlkode: "
"{code}"
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixbase/js/asynctask.js:125
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr "Forespørgselen tog for lang tid. Prøv venligst igen."
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixbase/js/asynctask.js:150
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
@@ -101,11 +113,11 @@ msgstr ""
"Vi kan ikke komme i kontakt med serveren. Prøv venligst igen. Fejlkode: "
"{code}"
#: pretix/static/pretixbase/js/asynctask.js:148
#: pretix/static/pretixbase/js/asynctask.js:171
msgid "We are processing your request …"
msgstr "Vi behandler din bestilling …"
#: pretix/static/pretixbase/js/asynctask.js:156
#: pretix/static/pretixbase/js/asynctask.js:179
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 "
@@ -114,8 +126,8 @@ msgstr ""
"Din forespørgsel bliver sendt til serveren. Hvis det tager mere end et "
"minut, så tjek din internetforbindelse, genindlæs siden og prøv igen."
#: pretix/static/pretixbase/js/asynctask.js:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
#: pretix/static/pretixbase/js/asynctask.js:216
#: pretix/static/pretixcontrol/js/ui/main.js:34
msgid "Close message"
msgstr "Luk besked"
@@ -184,37 +196,37 @@ msgstr "Der er sket en fejl."
msgid "Generating messages …"
msgstr "Opretter beskeder …"
#: pretix/static/pretixcontrol/js/ui/main.js:55
#: pretix/static/pretixcontrol/js/ui/main.js:69
msgid "Unknown error."
msgstr "Ukendt fejl."
#: pretix/static/pretixcontrol/js/ui/main.js:217
#: pretix/static/pretixcontrol/js/ui/main.js:231
msgid "Your color has great contrast and is very easy to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:221
#: pretix/static/pretixcontrol/js/ui/main.js:235
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:225
#: pretix/static/pretixcontrol/js/ui/main.js:239
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:305
#: pretix/static/pretixcontrol/js/ui/main.js:319
msgid "All"
msgstr "Alle"
#: pretix/static/pretixcontrol/js/ui/main.js:306
#: pretix/static/pretixcontrol/js/ui/main.js:320
msgid "None"
msgstr "Ingen"
#: pretix/static/pretixcontrol/js/ui/main.js:595
#: pretix/static/pretixcontrol/js/ui/main.js:609
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:652
#: pretix/static/pretixcontrol/js/ui/main.js:666
msgid "Click to close"
msgstr ""

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: 2019-06-25 09:08+0000\n"
"POT-Creation-Date: 2019-07-15 08:31+0000\n"
"PO-Revision-Date: 2019-05-01 12:13+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -47,12 +47,23 @@ msgstr "Gesamtumsatz"
msgid "Contacting Stripe …"
msgstr "Kontaktiere Stripe …"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
msgid "Total"
msgstr "Gesamt"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:144
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:175
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:151
#, fuzzy
#| msgid "Contacting Stripe …"
msgid "Contacting your bank …"
msgstr "Kontaktiere Stripe …"
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
#: pretix/static/pretixbase/js/asynctask.js:105
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."
@@ -61,7 +72,7 @@ msgstr ""
"Größe der Veranstaltung kann dies einige Minuten dauern."
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
#: pretix/static/pretixbase/js/asynctask.js:111
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 "
@@ -72,13 +83,14 @@ msgstr ""
"bitte oder gehen Sie in Ihrem Browser einen Schritt zurück und versuchen es "
"erneut."
#: pretix/static/pretixbase/js/asynctask.js:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixbase/js/asynctask.js:76
#: pretix/static/pretixbase/js/asynctask.js:142
#: pretix/static/pretixbase/js/asynctask.js:147
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr "Ein Fehler ist aufgetreten. Fehlercode: {code}"
#: pretix/static/pretixbase/js/asynctask.js:69
#: pretix/static/pretixbase/js/asynctask.js:79
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
@@ -86,12 +98,12 @@ msgstr ""
"Wir können den Server aktuell nicht erreichen, versuchen es aber weiter. "
"Letzter Fehlercode: {code}"
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixbase/js/asynctask.js:125
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr "Diese Anfrage hat zu lange gedauert. Bitte erneut versuchen."
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixbase/js/asynctask.js:150
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
@@ -99,11 +111,11 @@ msgstr ""
"Wir können den Server aktuell nicht erreichen. Bitte versuchen Sie es noch "
"einmal. Fehlercode: {code}"
#: pretix/static/pretixbase/js/asynctask.js:148
#: pretix/static/pretixbase/js/asynctask.js:171
msgid "We are processing your request …"
msgstr "Wir verarbeiten Ihre Anfrage …"
#: pretix/static/pretixbase/js/asynctask.js:156
#: pretix/static/pretixbase/js/asynctask.js:179
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 "
@@ -113,8 +125,8 @@ msgstr ""
"dauert, prüfen Sie bitte Ihre Internetverbindung. Danach können Sie diese "
"Seite neu laden und es erneut versuchen."
#: pretix/static/pretixbase/js/asynctask.js:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
#: pretix/static/pretixbase/js/asynctask.js:216
#: pretix/static/pretixcontrol/js/ui/main.js:34
msgid "Close message"
msgstr "Schließen"
@@ -184,20 +196,20 @@ msgstr "Ein Fehler ist aufgetreten."
msgid "Generating messages …"
msgstr "Generiere Nachrichten…"
#: pretix/static/pretixcontrol/js/ui/main.js:55
#: pretix/static/pretixcontrol/js/ui/main.js:69
msgid "Unknown error."
msgstr "Unbekannter Fehler."
#: pretix/static/pretixcontrol/js/ui/main.js:217
#: pretix/static/pretixcontrol/js/ui/main.js:231
msgid "Your color has great contrast and is very easy to read!"
msgstr "Diese Farbe hat einen sehr guten Kontrast und ist sehr gut zu lesen!"
#: pretix/static/pretixcontrol/js/ui/main.js:221
#: pretix/static/pretixcontrol/js/ui/main.js:235
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
"Diese Farbe hat einen ausreichenden Kontrast und wahrscheinlich gut zu lesen!"
#: pretix/static/pretixcontrol/js/ui/main.js:225
#: pretix/static/pretixcontrol/js/ui/main.js:239
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
@@ -205,19 +217,19 @@ msgstr ""
"Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen "
"Hintergrund. Bitte wählen Sie eine dunklere Farbe."
#: pretix/static/pretixcontrol/js/ui/main.js:305
#: pretix/static/pretixcontrol/js/ui/main.js:319
msgid "All"
msgstr "Alle"
#: pretix/static/pretixcontrol/js/ui/main.js:306
#: pretix/static/pretixcontrol/js/ui/main.js:320
msgid "None"
msgstr "Keine"
#: pretix/static/pretixcontrol/js/ui/main.js:595
#: pretix/static/pretixcontrol/js/ui/main.js:609
msgid "Use a different name internally"
msgstr "Intern einen anderen Namen verwenden"
#: pretix/static/pretixcontrol/js/ui/main.js:652
#: pretix/static/pretixcontrol/js/ui/main.js:666
msgid "Click to close"
msgstr "Klicken zum Schließen"

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: 2019-06-25 09:08+0000\n"
"POT-Creation-Date: 2019-07-15 08:31+0000\n"
"PO-Revision-Date: 2019-05-01 12:12+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
@@ -47,12 +47,23 @@ msgstr "Gesamtumsatz"
msgid "Contacting Stripe …"
msgstr "Kontaktiere Stripe …"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
msgid "Total"
msgstr "Gesamt"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:144
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:175
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:151
#, fuzzy
#| msgid "Contacting Stripe …"
msgid "Contacting your bank …"
msgstr "Kontaktiere Stripe …"
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
#: pretix/static/pretixbase/js/asynctask.js:105
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."
@@ -61,7 +72,7 @@ msgstr ""
"nach Größe der Veranstaltung kann dies einige Minuten dauern."
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
#: pretix/static/pretixbase/js/asynctask.js:111
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 "
@@ -71,13 +82,14 @@ msgstr ""
"verarbeitet. Wenn dies länger als zwei Minuten dauert, kontaktiere uns bitte "
"oder gehe in deinem Browser einen Schritt zurück und versuche es erneut."
#: pretix/static/pretixbase/js/asynctask.js:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixbase/js/asynctask.js:76
#: pretix/static/pretixbase/js/asynctask.js:142
#: pretix/static/pretixbase/js/asynctask.js:147
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr "Ein Fehler vom Typ {code} ist aufgetreten."
#: pretix/static/pretixbase/js/asynctask.js:69
#: pretix/static/pretixbase/js/asynctask.js:79
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
@@ -85,12 +97,12 @@ msgstr ""
"Wir können den Server aktuell nicht erreichen, versuchen es aber weiter. "
"Letzter Fehlercode: {code}"
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixbase/js/asynctask.js:125
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr "Diese Anfrage hat zu lange gedauert. Bitte erneut versuchen."
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixbase/js/asynctask.js:150
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
@@ -98,11 +110,11 @@ msgstr ""
"Wir können den Server aktuell nicht erreichen. Bitte versuche es noch "
"einmal. Fehlercode: {code}"
#: pretix/static/pretixbase/js/asynctask.js:148
#: pretix/static/pretixbase/js/asynctask.js:171
msgid "We are processing your request …"
msgstr "Wir verarbeiten deine Anfrage …"
#: pretix/static/pretixbase/js/asynctask.js:156
#: pretix/static/pretixbase/js/asynctask.js:179
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 "
@@ -112,8 +124,8 @@ msgstr ""
"dauert, prüfe bitte deine Internetverbindung. Danach kannst du diese Seite "
"neu laden und es erneut versuchen."
#: pretix/static/pretixbase/js/asynctask.js:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
#: pretix/static/pretixbase/js/asynctask.js:216
#: pretix/static/pretixcontrol/js/ui/main.js:34
msgid "Close message"
msgstr "Schließen"
@@ -183,20 +195,20 @@ msgstr "Ein Fehler ist aufgetreten."
msgid "Generating messages …"
msgstr "Generiere Nachrichten…"
#: pretix/static/pretixcontrol/js/ui/main.js:55
#: pretix/static/pretixcontrol/js/ui/main.js:69
msgid "Unknown error."
msgstr "Unbekannter Fehler."
#: pretix/static/pretixcontrol/js/ui/main.js:217
#: pretix/static/pretixcontrol/js/ui/main.js:231
msgid "Your color has great contrast and is very easy to read!"
msgstr "Diese Farbe hat einen sehr guten Kontrast und ist sehr gut zu lesen!"
#: pretix/static/pretixcontrol/js/ui/main.js:221
#: pretix/static/pretixcontrol/js/ui/main.js:235
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
"Diese Farbe hat einen ausreichenden Kontrast und wahrscheinlich gut zu lesen!"
#: pretix/static/pretixcontrol/js/ui/main.js:225
#: pretix/static/pretixcontrol/js/ui/main.js:239
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
@@ -204,19 +216,19 @@ msgstr ""
"Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen "
"Hintergrund. Bitte wähle eine dunklere Farbe."
#: pretix/static/pretixcontrol/js/ui/main.js:305
#: pretix/static/pretixcontrol/js/ui/main.js:319
msgid "All"
msgstr "Alle"
#: pretix/static/pretixcontrol/js/ui/main.js:306
#: pretix/static/pretixcontrol/js/ui/main.js:320
msgid "None"
msgstr "Keine"
#: pretix/static/pretixcontrol/js/ui/main.js:595
#: pretix/static/pretixcontrol/js/ui/main.js:609
msgid "Use a different name internally"
msgstr "Intern einen anderen Namen verwenden"
#: pretix/static/pretixcontrol/js/ui/main.js:652
#: pretix/static/pretixcontrol/js/ui/main.js:666
msgid "Click to close"
msgstr "Klicken zum Schließen"

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: 2019-06-25 09:08+0000\n"
"POT-Creation-Date: 2019-07-15 08:31+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"
@@ -46,61 +46,71 @@ msgstr ""
msgid "Contacting Stripe …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
msgid "Total"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:144
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:175
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:151
msgid "Contacting your bank …"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
#: pretix/static/pretixbase/js/asynctask.js:105
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."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
#: pretix/static/pretixbase/js/asynctask.js:111
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:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixbase/js/asynctask.js:76
#: pretix/static/pretixbase/js/asynctask.js:142
#: pretix/static/pretixbase/js/asynctask.js:147
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:69
#: pretix/static/pretixbase/js/asynctask.js:79
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixbase/js/asynctask.js:125
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixbase/js/asynctask.js:150
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:148
#: pretix/static/pretixbase/js/asynctask.js:171
msgid "We are processing your request …"
msgstr ""
#: pretix/static/pretixbase/js/asynctask.js:156
#: pretix/static/pretixbase/js/asynctask.js:179
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:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
#: pretix/static/pretixbase/js/asynctask.js:216
#: pretix/static/pretixcontrol/js/ui/main.js:34
msgid "Close message"
msgstr ""
@@ -168,37 +178,37 @@ msgstr ""
msgid "Generating messages …"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:55
#: pretix/static/pretixcontrol/js/ui/main.js:69
msgid "Unknown error."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:217
#: pretix/static/pretixcontrol/js/ui/main.js:231
msgid "Your color has great contrast and is very easy to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:221
#: pretix/static/pretixcontrol/js/ui/main.js:235
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:225
#: pretix/static/pretixcontrol/js/ui/main.js:239
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:305
#: pretix/static/pretixcontrol/js/ui/main.js:319
msgid "All"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:306
#: pretix/static/pretixcontrol/js/ui/main.js:320
msgid "None"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:595
#: pretix/static/pretixcontrol/js/ui/main.js:609
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:652
#: pretix/static/pretixcontrol/js/ui/main.js:666
msgid "Click to close"
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: 2019-06-25 09:08+0000\n"
"POT-Creation-Date: 2019-07-15 08:31+0000\n"
"PO-Revision-Date: 2019-06-04 16:00+0000\n"
"Last-Translator: ThanosTeste <testebasisth@unisystems.eu>\n"
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -47,12 +47,23 @@ msgstr "Συνολικά κέρδη"
msgid "Contacting Stripe …"
msgstr "Επικοινωνία με το Stripe …"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
msgid "Total"
msgstr "Σύνολο"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:144
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:175
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:151
#, fuzzy
#| msgid "Contacting Stripe …"
msgid "Contacting your bank …"
msgstr "Επικοινωνία με το Stripe …"
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
#: pretix/static/pretixbase/js/asynctask.js:105
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."
@@ -62,7 +73,7 @@ msgstr ""
"διαρκέσει μερικά λεπτά."
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
#: pretix/static/pretixbase/js/asynctask.js:111
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 "
@@ -72,13 +83,14 @@ msgstr ""
"του. Αν αυτό διαρκεί περισσότερο από δύο λεπτά, επικοινωνήστε μαζί μας ή "
"επιστρέψτε στο πρόγραμμα περιήγησής σας και δοκιμάστε ξανά."
#: pretix/static/pretixbase/js/asynctask.js:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixbase/js/asynctask.js:76
#: pretix/static/pretixbase/js/asynctask.js:142
#: pretix/static/pretixbase/js/asynctask.js:147
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr "Παρουσιάστηκε σφάλμα τύπου {code}."
#: pretix/static/pretixbase/js/asynctask.js:69
#: pretix/static/pretixbase/js/asynctask.js:79
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
@@ -86,12 +98,12 @@ msgstr ""
"Αυτήν τη στιγμή δεν μπορούμε να φτάσουμε στο διακομιστή, αλλά συνεχίζουμε να "
"προσπαθούμε. Τελευταίος κωδικός σφάλματος: {code}"
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixbase/js/asynctask.js:125
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr "Το αίτημα διήρκησε πολύ. Παρακαλώ προσπαθήστε ξανά."
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixbase/js/asynctask.js:150
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
@@ -99,11 +111,11 @@ msgstr ""
"Αυτήν τη στιγμή δεν μπορούμε να συνδεθούμε με το διακομιστή. Παρακαλώ "
"προσπαθήστε ξανά. Κωδικός σφάλματος: {code}"
#: pretix/static/pretixbase/js/asynctask.js:148
#: pretix/static/pretixbase/js/asynctask.js:171
msgid "We are processing your request …"
msgstr "Επεξεργαζόμαστε το αίτημά σας …"
#: pretix/static/pretixbase/js/asynctask.js:156
#: pretix/static/pretixbase/js/asynctask.js:179
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 "
@@ -113,8 +125,8 @@ msgstr ""
"περισσότερο από ένα λεπτό, ελέγξτε τη σύνδεσή σας στο διαδίκτυο και στη "
"συνέχεια επαναλάβετε τη φόρτωση αυτής της σελίδας και δοκιμάστε ξανά."
#: pretix/static/pretixbase/js/asynctask.js:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
#: pretix/static/pretixbase/js/asynctask.js:216
#: pretix/static/pretixcontrol/js/ui/main.js:34
msgid "Close message"
msgstr "Κλείσιμο μηνύματος"
@@ -185,22 +197,22 @@ msgstr "Παρουσιάστηκε σφάλμα."
msgid "Generating messages …"
msgstr "Δημιουργία μηνυμάτων …"
#: pretix/static/pretixcontrol/js/ui/main.js:55
#: pretix/static/pretixcontrol/js/ui/main.js:69
msgid "Unknown error."
msgstr "Αγνωστο σφάλμα."
#: pretix/static/pretixcontrol/js/ui/main.js:217
#: pretix/static/pretixcontrol/js/ui/main.js:231
msgid "Your color has great contrast and is very easy to read!"
msgstr ""
"Το χρώμα σας έχει μεγάλη αντίθεση και είναι πολύ εύκολο να το διαβάσετε!"
#: pretix/static/pretixcontrol/js/ui/main.js:221
#: pretix/static/pretixcontrol/js/ui/main.js:235
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
"Το χρώμα σας έχει αξιοπρεπή αντίθεση και είναι ίσως αρκετά καλό για να "
"διαβάσετε!"
#: pretix/static/pretixcontrol/js/ui/main.js:225
#: pretix/static/pretixcontrol/js/ui/main.js:239
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
@@ -208,19 +220,19 @@ msgstr ""
"Το χρώμα σας έχει κακή αντίθεση για κείμενο σε λευκό φόντο, επιλέξτε μια πιο "
"σκούρα σκιά."
#: pretix/static/pretixcontrol/js/ui/main.js:305
#: pretix/static/pretixcontrol/js/ui/main.js:319
msgid "All"
msgstr "Ολα"
#: pretix/static/pretixcontrol/js/ui/main.js:306
#: pretix/static/pretixcontrol/js/ui/main.js:320
msgid "None"
msgstr "Κανένας"
#: pretix/static/pretixcontrol/js/ui/main.js:595
#: pretix/static/pretixcontrol/js/ui/main.js:609
msgid "Use a different name internally"
msgstr "Χρησιμοποιήστε διαφορετικό όνομα εσωτερικά"
#: pretix/static/pretixcontrol/js/ui/main.js:652
#: pretix/static/pretixcontrol/js/ui/main.js:666
msgid "Click to close"
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: 2019-06-25 09:08+0000\n"
"POT-Creation-Date: 2019-07-15 08:31+0000\n"
"PO-Revision-Date: 2019-03-31 08:00+0000\n"
"Last-Translator: oocf <oswaldocerna@gmail.com>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -47,12 +47,23 @@ msgstr "Ingresos totales"
msgid "Contacting Stripe …"
msgstr "Contactando con Stripe…"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
msgid "Total"
msgstr "Total"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:144
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:175
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:151
#, fuzzy
#| msgid "Contacting Stripe …"
msgid "Contacting your bank …"
msgstr "Contactando con Stripe…"
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
#: pretix/static/pretixbase/js/asynctask.js:105
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."
@@ -61,7 +72,7 @@ msgstr ""
"puede tomar uno o varios minutos, en dependencia del tamaño de su evento."
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
#: pretix/static/pretixbase/js/asynctask.js:111
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 "
@@ -71,13 +82,14 @@ msgstr ""
"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."
#: pretix/static/pretixbase/js/asynctask.js:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixbase/js/asynctask.js:76
#: pretix/static/pretixbase/js/asynctask.js:142
#: pretix/static/pretixbase/js/asynctask.js:147
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr "Ha ocurrido un error de tipo {code}."
#: pretix/static/pretixbase/js/asynctask.js:69
#: pretix/static/pretixbase/js/asynctask.js:79
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
@@ -85,12 +97,12 @@ msgstr ""
"Ahora mismo no podemos contactar con el servidor, pero lo seguimos "
"intentando. El último código de error fue: {code}"
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixbase/js/asynctask.js:125
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr "La solicitud ha tomado demasiado tiempo. Por favor, pruebe de nuevo."
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixbase/js/asynctask.js:150
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
@@ -98,11 +110,11 @@ msgstr ""
"Ahora mismo no podemos contactar con el servidor. Por favor, pruebe de "
"nuevo. Código de error: {code}"
#: pretix/static/pretixbase/js/asynctask.js:148
#: pretix/static/pretixbase/js/asynctask.js:171
msgid "We are processing your request …"
msgstr "Estamos procesando su solicitud…"
#: pretix/static/pretixbase/js/asynctask.js:156
#: pretix/static/pretixbase/js/asynctask.js:179
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 "
@@ -112,8 +124,8 @@ msgstr ""
"minuto, por favor, revise su conexión a Internet, recargue la página y "
"pruebe de nuevo."
#: pretix/static/pretixbase/js/asynctask.js:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
#: pretix/static/pretixbase/js/asynctask.js:216
#: pretix/static/pretixcontrol/js/ui/main.js:34
msgid "Close message"
msgstr "Cerrar mensaje"
@@ -184,21 +196,21 @@ msgstr "Ha ocurrido un error."
msgid "Generating messages …"
msgstr "Generando mensajes…"
#: pretix/static/pretixcontrol/js/ui/main.js:55
#: pretix/static/pretixcontrol/js/ui/main.js:69
msgid "Unknown error."
msgstr "Error desconocido."
#: pretix/static/pretixcontrol/js/ui/main.js:217
#: pretix/static/pretixcontrol/js/ui/main.js:231
msgid "Your color has great contrast and is very easy to read!"
msgstr "¡Tu color tiene gran contraste y es muy legible!"
#: pretix/static/pretixcontrol/js/ui/main.js:221
#: pretix/static/pretixcontrol/js/ui/main.js:235
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
"¡Tu color tiene un contraste decente y es probablemente suficientemente "
"legible!"
#: pretix/static/pretixcontrol/js/ui/main.js:225
#: pretix/static/pretixcontrol/js/ui/main.js:239
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
@@ -206,19 +218,19 @@ msgstr ""
"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:305
#: pretix/static/pretixcontrol/js/ui/main.js:319
msgid "All"
msgstr "Todos"
#: pretix/static/pretixcontrol/js/ui/main.js:306
#: pretix/static/pretixcontrol/js/ui/main.js:320
msgid "None"
msgstr "Ninguno"
#: pretix/static/pretixcontrol/js/ui/main.js:595
#: pretix/static/pretixcontrol/js/ui/main.js:609
msgid "Use a different name internally"
msgstr "Usar un nombre diferente internamente"
#: pretix/static/pretixcontrol/js/ui/main.js:652
#: pretix/static/pretixcontrol/js/ui/main.js:666
msgid "Click to close"
msgstr "Click para cerrar"

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: 2019-06-25 09:08+0000\n"
"POT-Creation-Date: 2019-07-15 08:31+0000\n"
"PO-Revision-Date: 2018-10-28 10:23+0000\n"
"Last-Translator: Arnaud Vergnet <keplyx@gmail.com>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -46,12 +46,23 @@ msgstr "Revenu total"
msgid "Contacting Stripe …"
msgstr "Contacter Stripe …"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:56
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
msgid "Total"
msgstr "Total"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:144
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:175
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:151
#, fuzzy
#| msgid "Contacting Stripe …"
msgid "Contacting your bank …"
msgstr "Contacter Stripe …"
#: pretix/static/pretixbase/js/asynctask.js:39
#: pretix/static/pretixbase/js/asynctask.js:95
#: pretix/static/pretixbase/js/asynctask.js:105
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."
@@ -60,7 +71,7 @@ msgstr ""
"taille de votre événement, cela peut prendre jusqu' à quelques minutes."
#: pretix/static/pretixbase/js/asynctask.js:45
#: pretix/static/pretixbase/js/asynctask.js:101
#: pretix/static/pretixbase/js/asynctask.js:111
#, fuzzy
#| msgid ""
#| "Your request has been queued on the server and will now be processed. If "
@@ -75,13 +86,14 @@ msgstr ""
"prend plus de deux minutes, veuillez nous contacter ou retourner dans votre "
"navigateur et réessayer."
#: pretix/static/pretixbase/js/asynctask.js:66
#: pretix/static/pretixbase/js/asynctask.js:124
#: pretix/static/pretixbase/js/asynctask.js:76
#: pretix/static/pretixbase/js/asynctask.js:142
#: pretix/static/pretixbase/js/asynctask.js:147
#: pretix/static/pretixcontrol/js/ui/mail.js:23
msgid "An error of type {code} occurred."
msgstr "Une erreur de type {code} s'est produite."
#: pretix/static/pretixbase/js/asynctask.js:69
#: pretix/static/pretixbase/js/asynctask.js:79
msgid ""
"We currently cannot reach the server, but we keep trying. Last error code: "
"{code}"
@@ -89,12 +101,12 @@ msgstr ""
"Nous ne pouvons actuellement pas atteindre le serveur, mais nous continuons "
"d'essayer. Dernier code d'erreur: {code}"
#: pretix/static/pretixbase/js/asynctask.js:115
#: pretix/static/pretixbase/js/asynctask.js:125
#: pretix/static/pretixcontrol/js/ui/mail.js:20
msgid "The request took to long. Please try again."
msgstr "La requête a prit trop de temps. Veuillez réessayer."
#: pretix/static/pretixbase/js/asynctask.js:127
#: pretix/static/pretixbase/js/asynctask.js:150
#: pretix/static/pretixcontrol/js/ui/mail.js:25
msgid ""
"We currently cannot reach the server. Please try again. Error code: {code}"
@@ -102,11 +114,11 @@ msgstr ""
"Actuellement, nous ne pouvons pas atteindre le serveur. Veuillez réessayer. "
"Code d'erreur: {code}"
#: pretix/static/pretixbase/js/asynctask.js:148
#: pretix/static/pretixbase/js/asynctask.js:171
msgid "We are processing your request …"
msgstr "Nous traitons votre demande …"
#: pretix/static/pretixbase/js/asynctask.js:156
#: pretix/static/pretixbase/js/asynctask.js:179
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 "
@@ -116,8 +128,8 @@ msgstr ""
"d'une minute, veuillez vérifier votre connexion Internet, puis recharger "
"cette page et réessayer."
#: pretix/static/pretixbase/js/asynctask.js:193
#: pretix/static/pretixcontrol/js/ui/main.js:20
#: pretix/static/pretixbase/js/asynctask.js:216
#: pretix/static/pretixcontrol/js/ui/main.js:34
msgid "Close message"
msgstr "Fermer le message"
@@ -189,37 +201,37 @@ msgstr "Une erreur s'est produite."
msgid "Generating messages …"
msgstr "Création de messages …"
#: pretix/static/pretixcontrol/js/ui/main.js:55
#: pretix/static/pretixcontrol/js/ui/main.js:69
msgid "Unknown error."
msgstr "Erreur inconnue."
#: pretix/static/pretixcontrol/js/ui/main.js:217
#: pretix/static/pretixcontrol/js/ui/main.js:231
msgid "Your color has great contrast and is very easy to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:221
#: pretix/static/pretixcontrol/js/ui/main.js:235
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:225
#: pretix/static/pretixcontrol/js/ui/main.js:239
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:305
#: pretix/static/pretixcontrol/js/ui/main.js:319
msgid "All"
msgstr "Tous"
#: pretix/static/pretixcontrol/js/ui/main.js:306
#: pretix/static/pretixcontrol/js/ui/main.js:320
msgid "None"
msgstr "Aucun"
#: pretix/static/pretixcontrol/js/ui/main.js:595
#: pretix/static/pretixcontrol/js/ui/main.js:609
msgid "Use a different name internally"
msgstr "Utiliser un nom différent en interne"
#: pretix/static/pretixcontrol/js/ui/main.js:652
#: pretix/static/pretixcontrol/js/ui/main.js:666
msgid "Click to close"
msgstr ""

File diff suppressed because it is too large Load Diff

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