mirror of
https://github.com/pretix/pretix.git
synced 2026-02-24 09:42:27 +00:00
Compare commits
135 Commits
v3.11.0
...
stripe-con
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
951e498089 | ||
|
|
b4a7729cb5 | ||
|
|
f2e5e89970 | ||
|
|
4fd773caf6 | ||
|
|
6402f0d86e | ||
|
|
f5d93eaffa | ||
|
|
3f40a8e6fa | ||
|
|
b947467589 | ||
|
|
810f3d7d31 | ||
|
|
e8f3ad633a | ||
|
|
301849f771 | ||
|
|
ee6a595e26 | ||
|
|
980296e38a | ||
|
|
0a62ee0e33 | ||
|
|
afc1013d69 | ||
|
|
16cf3cec76 | ||
|
|
0105b9642d | ||
|
|
3ec15fa529 | ||
|
|
703eebab47 | ||
|
|
3aec3a52fc | ||
|
|
fd93cac8cf | ||
|
|
e139924696 | ||
|
|
da725c0bff | ||
|
|
dca61447cf | ||
|
|
f54bf3f1ea | ||
|
|
3cef9bac26 | ||
|
|
4f20849e4b | ||
|
|
758981fc1b | ||
|
|
9b671d6370 | ||
|
|
3bfaf55094 | ||
|
|
3708dab656 | ||
|
|
14ad7716bd | ||
|
|
985d3c3993 | ||
|
|
fa2222e629 | ||
|
|
13eabdd7f4 | ||
|
|
4fd748e6d9 | ||
|
|
f48ded0165 | ||
|
|
903ea09140 | ||
|
|
fadc610b8e | ||
|
|
ac4b8a392b | ||
|
|
22d986a709 | ||
|
|
bca34145f1 | ||
|
|
97af6f7311 | ||
|
|
67156a67aa | ||
|
|
4ed872d4ef | ||
|
|
5cd6cba0a2 | ||
|
|
72bb5bd177 | ||
|
|
d392e14a96 | ||
|
|
d7459b3b83 | ||
|
|
b4778b5845 | ||
|
|
5a09759cb9 | ||
|
|
2fbaa90d76 | ||
|
|
93f10d33a9 | ||
|
|
e9a972ad60 | ||
|
|
a31f0c1bc8 | ||
|
|
1b0c2f3bb7 | ||
|
|
766428c469 | ||
|
|
d85583f70a | ||
|
|
ee801bd717 | ||
|
|
af0e8ec992 | ||
|
|
bc3325c1cb | ||
|
|
753c331887 | ||
|
|
cfc9055ec1 | ||
|
|
c131a2ac3a | ||
|
|
17fe3355d1 | ||
|
|
0381d42d41 | ||
|
|
b73db911e9 | ||
|
|
1f3d4a2810 | ||
|
|
3cbcf663e5 | ||
|
|
ae0637a3d6 | ||
|
|
a6a9c08a0a | ||
|
|
f3b3d0b8f7 | ||
|
|
9490f20a6c | ||
|
|
4555a917b2 | ||
|
|
951e99d0da | ||
|
|
d0b002cf0c | ||
|
|
4fb0b948ec | ||
|
|
2384478b45 | ||
|
|
f3a2d0cb03 | ||
|
|
1b11d88442 | ||
|
|
954951ddfa | ||
|
|
c01b96bdfc | ||
|
|
c78e88a1ba | ||
|
|
4cb18218b2 | ||
|
|
450d017c32 | ||
|
|
655977e33d | ||
|
|
0cb0620df0 | ||
|
|
c8bf069650 | ||
|
|
e65087fd68 | ||
|
|
d67d389b9d | ||
|
|
0e805e50f9 | ||
|
|
a4d133731e | ||
|
|
c74e7fd4fb | ||
|
|
0e405d2327 | ||
|
|
035c707427 | ||
|
|
787e7ec993 | ||
|
|
09a9b4a456 | ||
|
|
e2547c2761 | ||
|
|
c7b2baf40f | ||
|
|
59595c9db8 | ||
|
|
2f8baecd68 | ||
|
|
a76f74b161 | ||
|
|
f2518101ef | ||
|
|
ec667545e8 | ||
|
|
afb789226c | ||
|
|
bca7a6db93 | ||
|
|
429ad4da37 | ||
|
|
cd6e6004af | ||
|
|
e9d5665a3d | ||
|
|
4cbc30a7ea | ||
|
|
2b0388c2ee | ||
|
|
06b8826e57 | ||
|
|
7c6f0f45a3 | ||
|
|
93399f51b3 | ||
|
|
87bd54b233 | ||
|
|
3fb237f434 | ||
|
|
d7640d25f5 | ||
|
|
1669d3f5c7 | ||
|
|
5aa3f3e772 | ||
|
|
b7a2f0257f | ||
|
|
5c0f29f959 | ||
|
|
59655dca82 | ||
|
|
af2b4ebb4b | ||
|
|
d5b3528f92 | ||
|
|
0a1b41235b | ||
|
|
8ca544064b | ||
|
|
1e2b305376 | ||
|
|
bfa20e995a | ||
|
|
e7fd0f116b | ||
|
|
e836da09cd | ||
|
|
22c6553a48 | ||
|
|
ea5fc3df40 | ||
|
|
7977b6dc15 | ||
|
|
59df5fe052 | ||
|
|
c4e00e7601 |
@@ -97,6 +97,9 @@ Example::
|
||||
|
||||
``csp_log``
|
||||
Log violations of the Content Security Policy (CSP). Defaults to ``on``.
|
||||
|
||||
``loglevel``
|
||||
Set console and file loglevel (``DEBUG``, ``INFO``, ``WARNING``, ``ERROR`` or ``CRITICAL``). Defaults to ``INFO``.
|
||||
|
||||
Locale settings
|
||||
---------------
|
||||
|
||||
@@ -25,7 +25,7 @@ Obtaining an authorization grant
|
||||
--------------------------------
|
||||
|
||||
To authorize a new user, link or redirect them to the ``authorize`` endpoint, passing your client ID as a query
|
||||
parameter. Additionally, you can pass a scope (currently either ``read``, ``write``, or ``read write``)
|
||||
parameter. Additionally, you can pass a scope (currently either ``read``, ``write``, ``read write`` or ``profile``)
|
||||
and an URL the user should be redirected to after successful or failed authorization. You also need to pass the
|
||||
``response_type`` parameter with a value of ``code``. Example::
|
||||
|
||||
@@ -47,11 +47,9 @@ You will need this ``code`` parameter to perform the next step.
|
||||
|
||||
On a failed registration, a query string like ``?error=access_denied`` will be appended to the redirection URL.
|
||||
|
||||
.. note:: In this step, the user is allowed to restrict your access to certain organizer accounts. If you try to
|
||||
re-authenticate the user later, the user might be instantly redirected back to you if authorization is already
|
||||
given and would therefore be unable to review their organizer restriction settings. You can append the
|
||||
``approval_prompt=force`` query parameter if you want to make sure the user actively needs to confirm the
|
||||
authorization.
|
||||
.. note:: By default, the user is asked to give permission on every call to this URL. If you **only** request the
|
||||
``profile`` scope, i.e. no access to organizer data, you can pass the ``approval_prompt=auto`` parameter
|
||||
to skip user interaction on subsequen calls.
|
||||
|
||||
Getting an access token
|
||||
-----------------------
|
||||
@@ -193,10 +191,11 @@ If you need the user's meta data, you can fetch it here:
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
email: "admin@localhost",
|
||||
fullname: "John Doe",
|
||||
locale: "de",
|
||||
timezone: "Europe/Berlin"
|
||||
"email": "admin@localhost",
|
||||
"fullname": "John Doe",
|
||||
"locale": "de",
|
||||
"is_staff": false,
|
||||
"timezone": "Europe/Berlin"
|
||||
}
|
||||
|
||||
:statuscode 200: no error
|
||||
|
||||
224
doc/api/resources/devices.rst
Normal file
224
doc/api/resources/devices.rst
Normal file
@@ -0,0 +1,224 @@
|
||||
.. spelling:: fullname
|
||||
|
||||
.. _`rest-devices`:
|
||||
|
||||
Devices
|
||||
=======
|
||||
|
||||
See also :ref:`rest-deviceauth`.
|
||||
|
||||
Device resource
|
||||
----------------
|
||||
|
||||
The device resource contains the following public fields:
|
||||
|
||||
.. rst-class:: rest-resource-table
|
||||
|
||||
===================================== ========================== =======================================================
|
||||
Field Type Description
|
||||
===================================== ========================== =======================================================
|
||||
device_id integer Internal ID of the device within this organizer
|
||||
unique_serial string Unique identifier of this device
|
||||
name string Device name
|
||||
all_events boolean Whether this device has access to all events
|
||||
limit_events list List of event slugs this device has access to
|
||||
hardware_brand string Device hardware manufacturer (read-only)
|
||||
hardware_model string Device hardware model (read-only)
|
||||
software_brand string Device software product (read-only)
|
||||
software_version string Device software version (read-only)
|
||||
created datetime Creation time
|
||||
initialized datetime Time of initialization (or ``null``)
|
||||
initialization_token string Token for initialization
|
||||
revoked boolean Whether this device no longer has access
|
||||
security_profile string The name of a supported security profile restricting API access
|
||||
===================================== ========================== =======================================================
|
||||
|
||||
Device endpoints
|
||||
----------------
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/devices/
|
||||
|
||||
Returns a list of all devices within a given organizer.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/devices/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"count": 1,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"device_id": 1,
|
||||
"unique_serial": "UOS3GNZ27O39V3QS",
|
||||
"initialization_token": "frkso3m2w58zuw70",
|
||||
"all_events": false,
|
||||
"limit_events": [
|
||||
"museum"
|
||||
],
|
||||
"revoked": false,
|
||||
"name": "Scanner",
|
||||
"created": "2020-09-18T14:17:40.971519Z",
|
||||
"initialized": "2020-09-18T14:17:44.190021Z",
|
||||
"security_profile": "full",
|
||||
"hardware_brand": "Zebra",
|
||||
"hardware_model": "TC25",
|
||||
"software_brand": "pretixSCAN",
|
||||
"software_version": "1.5.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:statuscode 200: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/devices/(device_id)/
|
||||
|
||||
Returns information on one device, identified by its ID.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/devices/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"device_id": 1,
|
||||
"unique_serial": "UOS3GNZ27O39V3QS",
|
||||
"initialization_token": "frkso3m2w58zuw70",
|
||||
"all_events": false,
|
||||
"limit_events": [
|
||||
"museum"
|
||||
],
|
||||
"revoked": false,
|
||||
"name": "Scanner",
|
||||
"created": "2020-09-18T14:17:40.971519Z",
|
||||
"initialized": "2020-09-18T14:17:44.190021Z",
|
||||
"security_profile": "full",
|
||||
"hardware_brand": "Zebra",
|
||||
"hardware_model": "TC25",
|
||||
"software_brand": "pretixSCAN",
|
||||
"software_version": "1.5.1"
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param device_id: The ``device_id`` field of the device to fetch
|
||||
:statuscode 200: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
|
||||
|
||||
.. http:post:: /api/v1/organizers/(organizer)/devices/
|
||||
|
||||
Creates a new device
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1/organizers/bigevents/devices/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Scanner",
|
||||
"all_events": true,
|
||||
"limit_events": [],
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"device_id": 1,
|
||||
"unique_serial": "UOS3GNZ27O39V3QS",
|
||||
"initialization_token": "frkso3m2w58zuw70",
|
||||
"all_events": true,
|
||||
"limit_events": [],
|
||||
"revoked": false,
|
||||
"name": "Scanner",
|
||||
"created": "2020-09-18T14:17:40.971519Z",
|
||||
"security_profile": "full",
|
||||
"initialized": null
|
||||
"hardware_brand": null,
|
||||
"hardware_model": null,
|
||||
"software_brand": null,
|
||||
"software_version": null
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to create a device for
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: The device could not be created due to invalid submitted data.
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
|
||||
|
||||
.. http:patch:: /api/v1/organizers/(organizer)/devices/(device_id)/
|
||||
|
||||
Update a device.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PATCH /api/v1/organizers/bigevents/devices/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
Content-Length: 94
|
||||
|
||||
{
|
||||
"name": "Foo"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Foo",
|
||||
...
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to modify
|
||||
:param device_id: The ``device_id`` field of the deviec to modify
|
||||
:statuscode 200: no error
|
||||
:statuscode 400: The device could not be modified due to invalid submitted data
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource.
|
||||
|
||||
@@ -209,14 +209,15 @@ Endpoints
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PATCH /api/v1/organizers/bigevents/giftcards/1/transact/ HTTP/1.1
|
||||
POST /api/v1/organizers/bigevents/giftcards/1/transact/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
Content-Length: 94
|
||||
Content-Length: 79
|
||||
|
||||
{
|
||||
"value": "2.00"
|
||||
"value": "2.00",
|
||||
"text": "Optional value explaining the transaction"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -24,6 +24,7 @@ Resources and endpoints
|
||||
giftcards
|
||||
carts
|
||||
teams
|
||||
devices
|
||||
webhooks
|
||||
seatingplans
|
||||
billing_invoices
|
||||
|
||||
@@ -201,6 +201,7 @@ addon_to integer Internal ID of
|
||||
subevent integer ID of the date inside an event series this position belongs to (or ``null``).
|
||||
pseudonymization_id string A random ID, e.g. for use in lead scanning apps
|
||||
checkins list of objects List of check-ins with this ticket
|
||||
├ id integer Internal ID of the check-in event
|
||||
├ list integer Internal ID of the check-in list
|
||||
├ datetime datetime Time of check-in
|
||||
├ type string Type of scan (defaults to ``entry``)
|
||||
@@ -1029,6 +1030,10 @@ Creating orders
|
||||
Order state operations
|
||||
----------------------
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
|
||||
The ``mark_paid`` operation now takes a ``send_email`` parameter.
|
||||
|
||||
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/mark_paid/
|
||||
|
||||
Marks a pending or expired order as successfully paid.
|
||||
@@ -1040,6 +1045,11 @@ Order state operations
|
||||
POST /api/v1/organizers/bigevents/events/sampleconf/orders/ABC12/mark_paid/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"send_email": true
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
@@ -1722,6 +1732,10 @@ Order payment endpoints
|
||||
|
||||
Payments can now be created through the API.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
|
||||
The ``confirm`` operation now takes a ``send_email`` parameter.
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/payments/
|
||||
|
||||
Returns a list of all payments for an order.
|
||||
@@ -1822,7 +1836,10 @@ Order payment endpoints
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{"force": false}
|
||||
{
|
||||
"send_email": true,
|
||||
"force": false
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ in the ``installed`` method::
|
||||
pass # Your code here
|
||||
|
||||
|
||||
Note that ``installed`` will *not* be called if the plugin in indirectly activated for an event
|
||||
Note that ``installed`` will *not* be called if the plugin is indirectly activated for an event
|
||||
because the event is created with settings copied from another event.
|
||||
|
||||
Views
|
||||
@@ -151,8 +151,8 @@ your Django app label.
|
||||
with checking that the calling user is logged in, has appropriate permissions,
|
||||
etc. We plan on providing native support for this in a later version.
|
||||
|
||||
.. _Django app: https://docs.djangoproject.com/en/1.7/ref/applications/
|
||||
.. _signal dispatcher: https://docs.djangoproject.com/en/1.7/topics/signals/
|
||||
.. _namespace packages: http://legacy.python.org/dev/peps/pep-0420/
|
||||
.. _Django app: https://docs.djangoproject.com/en/3.0/ref/applications/
|
||||
.. _signal dispatcher: https://docs.djangoproject.com/en/3.0/topics/signals/
|
||||
.. _namespace packages: https://legacy.python.org/dev/peps/pep-0420/
|
||||
.. _entry point: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#locating-plugins
|
||||
.. _cookiecutter: https://cookiecutter.readthedocs.io/en/latest/
|
||||
|
||||
@@ -3,6 +3,7 @@ include README.rst
|
||||
recursive-include pretix/static *
|
||||
recursive-include pretix/static.dist *
|
||||
recursive-include pretix/locale *
|
||||
recursive-include pretix/helpers/locale *
|
||||
recursive-include pretix/base/templates *
|
||||
recursive-include pretix/control/templates *
|
||||
recursive-include pretix/presale/templates *
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "3.11.0"
|
||||
__version__ = "3.12.0.dev0"
|
||||
|
||||
@@ -3,6 +3,9 @@ from django_scopes import scopes_disabled
|
||||
from rest_framework import exceptions
|
||||
from rest_framework.authentication import TokenAuthentication
|
||||
|
||||
from pretix.api.auth.devicesecurity import (
|
||||
DEVICE_SECURITY_PROFILES, FullAccessSecurityProfile,
|
||||
)
|
||||
from pretix.base.models import Device
|
||||
|
||||
|
||||
@@ -25,3 +28,11 @@ class DeviceTokenAuthentication(TokenAuthentication):
|
||||
raise exceptions.AuthenticationFailed('Device access has been revoked.')
|
||||
|
||||
return AnonymousUser(), device
|
||||
|
||||
def authenticate(self, request):
|
||||
r = super().authenticate(request)
|
||||
if r and isinstance(r[1], Device):
|
||||
profile = DEVICE_SECURITY_PROFILES.get(r[1].security_profile, FullAccessSecurityProfile)
|
||||
if not profile.is_allowed(request):
|
||||
raise exceptions.PermissionDenied('Request denied by device security profile.')
|
||||
return r
|
||||
|
||||
112
src/pretix/api/auth/devicesecurity.py
Normal file
112
src/pretix/api/auth/devicesecurity.py
Normal file
@@ -0,0 +1,112 @@
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class FullAccessSecurityProfile:
|
||||
identifier = 'full'
|
||||
verbose_name = _('Full access')
|
||||
|
||||
def is_allowed(self, request):
|
||||
return True
|
||||
|
||||
|
||||
class AllowListSecurityProfile:
|
||||
allowlist = tuple()
|
||||
|
||||
def is_allowed(self, request):
|
||||
key = (request.method, f"{request.resolver_match.namespace}:{request.resolver_match.url_name}")
|
||||
return key in self.allowlist
|
||||
|
||||
|
||||
class PretixScanSecurityProfile(AllowListSecurityProfile):
|
||||
identifier = 'pretixscan'
|
||||
verbose_name = _('pretixSCAN')
|
||||
allowlist = (
|
||||
('GET', 'api-v1:version'),
|
||||
('GET', 'api-v1:device.update'),
|
||||
('GET', 'api-v1:device.revoke'),
|
||||
('GET', 'api-v1:event-list'),
|
||||
('GET', 'api-v1:event-detail'),
|
||||
('GET', 'api-v1:subevent-list'),
|
||||
('GET', 'api-v1:subevent-detail'),
|
||||
('GET', 'api-v1:itemcategory-list'),
|
||||
('GET', 'api-v1:item-list'),
|
||||
('GET', 'api-v1:question-list'),
|
||||
('GET', 'api-v1:badgelayout-list'),
|
||||
('GET', 'api-v1:badgeitem-list'),
|
||||
('GET', 'api-v1:checkinlist-list'),
|
||||
('GET', 'api-v1:checkinlist-status'),
|
||||
('GET', 'api-v1:checkinlistpos-list'),
|
||||
('POST', 'api-v1:checkinlistpos-redeem'),
|
||||
('GET', 'api-v1:order-list'),
|
||||
('GET', 'api-v1:event.settings'),
|
||||
)
|
||||
|
||||
|
||||
class PretixScanNoSyncSecurityProfile(AllowListSecurityProfile):
|
||||
identifier = 'pretixscan_online_kiosk'
|
||||
verbose_name = _('pretixSCAN (kiosk mode, online only)')
|
||||
allowlist = (
|
||||
('GET', 'api-v1:version'),
|
||||
('GET', 'api-v1:device.update'),
|
||||
('GET', 'api-v1:device.revoke'),
|
||||
('GET', 'api-v1:event-list'),
|
||||
('GET', 'api-v1:event-detail'),
|
||||
('GET', 'api-v1:subevent-list'),
|
||||
('GET', 'api-v1:subevent-detail'),
|
||||
('GET', 'api-v1:itemcategory-list'),
|
||||
('GET', 'api-v1:item-list'),
|
||||
('GET', 'api-v1:question-list'),
|
||||
('GET', 'api-v1:badgelayout-list'),
|
||||
('GET', 'api-v1:badgeitem-list'),
|
||||
('GET', 'api-v1:checkinlist-list'),
|
||||
('GET', 'api-v1:checkinlist-status'),
|
||||
('POST', 'api-v1:checkinlistpos-redeem'),
|
||||
('GET', 'api-v1:event.settings'),
|
||||
)
|
||||
|
||||
|
||||
class PretixPosSecurityProfile(AllowListSecurityProfile):
|
||||
identifier = 'pretixpos'
|
||||
verbose_name = _('pretixPOS')
|
||||
allowlist = (
|
||||
('GET', 'api-v1:version'),
|
||||
('GET', 'api-v1:device.update'),
|
||||
('GET', 'api-v1:device.revoke'),
|
||||
('GET', 'api-v1:event-list'),
|
||||
('GET', 'api-v1:event-detail'),
|
||||
('GET', 'api-v1:subevent-list'),
|
||||
('GET', 'api-v1:subevent-detail'),
|
||||
('GET', 'api-v1:itemcategory-list'),
|
||||
('GET', 'api-v1:item-list'),
|
||||
('GET', 'api-v1:question-list'),
|
||||
('GET', 'api-v1:quota-list'),
|
||||
('GET', 'api-v1:taxrule-list'),
|
||||
('GET', 'api-v1:ticketlayout-list'),
|
||||
('GET', 'api-v1:ticketlayoutitem-list'),
|
||||
('GET', 'api-v1:order-list'),
|
||||
('POST', 'api-v1:order-list'),
|
||||
('GET', 'api-v1:order-detail'),
|
||||
('DELETE', 'api-v1:orderposition-detail'),
|
||||
('POST', 'api-v1:order-mark_canceled'),
|
||||
('POST', 'api-v1:orderrefund-list'),
|
||||
('POST', 'api-v1:orderrefund-done'),
|
||||
('POST', 'api-v1:cartposition-list'),
|
||||
('DELETE', 'api-v1:cartposition-detail'),
|
||||
('GET', 'api-v1:giftcard-list'),
|
||||
('POST', 'api-v1:giftcard-transact'),
|
||||
('POST', 'plugins:pretix_posbackend:posreceipt-list'),
|
||||
('POST', 'plugins:pretix_posbackend:posclosing-list'),
|
||||
('POST', 'plugins:pretix_posbackend:posdebugdump-list'),
|
||||
('POST', 'plugins:pretix_posbackend:stripeterminal.token'),
|
||||
('GET', 'api-v1:event.settings'),
|
||||
)
|
||||
|
||||
|
||||
DEVICE_SECURITY_PROFILES = {
|
||||
k.identifier: k() for k in (
|
||||
FullAccessSecurityProfile,
|
||||
PretixScanSecurityProfile,
|
||||
PretixScanNoSyncSecurityProfile,
|
||||
PretixPosSecurityProfile,
|
||||
)
|
||||
}
|
||||
@@ -84,3 +84,15 @@ class EventCRUDPermission(EventPermission):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class ProfilePermission(BasePermission):
|
||||
|
||||
def has_permission(self, request, view):
|
||||
if not request.user.is_authenticated:
|
||||
return False
|
||||
|
||||
if isinstance(request.auth, OAuthAccessToken):
|
||||
if not (request.auth.allow_scopes(['read']) or request.auth.allow_scopes(['profile'])) and request.method in SAFE_METHODS:
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -9,7 +9,7 @@ from oauth2_provider.settings import oauth2_settings
|
||||
class Validator(OAuth2Validator):
|
||||
|
||||
def save_authorization_code(self, client_id, code, request, *args, **kwargs):
|
||||
if not getattr(request, 'organizers', None):
|
||||
if not getattr(request, 'organizers', None) and request.scopes != ['profile']:
|
||||
raise FatalClientError('No organizers selected.')
|
||||
|
||||
expires = timezone.now() + timedelta(
|
||||
@@ -18,7 +18,8 @@ class Validator(OAuth2Validator):
|
||||
expires=expires, redirect_uri=request.redirect_uri,
|
||||
scope=" ".join(request.scopes))
|
||||
g.save()
|
||||
g.organizers.add(*request.organizers.all())
|
||||
if request.scopes != ['profile']:
|
||||
g.organizers.add(*request.organizers.all())
|
||||
|
||||
def validate_code(self, client_id, code, client, request, *args, **kwargs):
|
||||
try:
|
||||
@@ -34,12 +35,14 @@ class Validator(OAuth2Validator):
|
||||
return False
|
||||
|
||||
def _create_access_token(self, expires, request, token, source_refresh_token=None):
|
||||
if not getattr(request, 'organizers', None) and not getattr(source_refresh_token, 'access_token'):
|
||||
if not getattr(request, 'organizers', None) and not getattr(source_refresh_token, 'access_token', None) and token["scope"] != 'profile':
|
||||
raise FatalClientError('No organizers selected.')
|
||||
if hasattr(request, 'organizers'):
|
||||
orgs = list(request.organizers.all())
|
||||
else:
|
||||
orgs = list(source_refresh_token.access_token.organizers.all())
|
||||
if token['scope'] != 'profile':
|
||||
if hasattr(request, 'organizers'):
|
||||
orgs = list(request.organizers.all())
|
||||
else:
|
||||
orgs = list(source_refresh_token.access_token.organizers.all())
|
||||
access_token = super()._create_access_token(expires, request, token, source_refresh_token=None)
|
||||
access_token.organizers.add(*orgs)
|
||||
if token['scope'] != 'profile':
|
||||
access_token.organizers.add(*orgs)
|
||||
return access_token
|
||||
|
||||
@@ -122,7 +122,7 @@ class AnswerSerializer(I18nAwareModelSerializer):
|
||||
class CheckinSerializer(I18nAwareModelSerializer):
|
||||
class Meta:
|
||||
model = Checkin
|
||||
fields = ('datetime', 'list', 'auto_checked_in', 'type')
|
||||
fields = ('id', 'datetime', 'list', 'auto_checked_in', 'type')
|
||||
|
||||
|
||||
class OrderDownloadsField(serializers.Field):
|
||||
|
||||
@@ -9,7 +9,8 @@ from pretix.api.serializers.i18n import I18nAwareModelSerializer
|
||||
from pretix.api.serializers.order import CompatibleJSONField
|
||||
from pretix.base.auth import get_auth_backends
|
||||
from pretix.base.models import (
|
||||
GiftCard, Organizer, SeatingPlan, Team, TeamAPIToken, TeamInvite, User,
|
||||
Device, GiftCard, Organizer, SeatingPlan, Team, TeamAPIToken, TeamInvite,
|
||||
User,
|
||||
)
|
||||
from pretix.base.models.seating import SeatingPlanLayoutValidator
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
@@ -66,9 +67,6 @@ class EventSlugField(serializers.SlugRelatedField):
|
||||
class TeamSerializer(serializers.ModelSerializer):
|
||||
limit_events = EventSlugField(slug_field='slug', many=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
model = Team
|
||||
fields = (
|
||||
@@ -86,6 +84,28 @@ class TeamSerializer(serializers.ModelSerializer):
|
||||
return data
|
||||
|
||||
|
||||
class DeviceSerializer(serializers.ModelSerializer):
|
||||
limit_events = EventSlugField(slug_field='slug', many=True)
|
||||
device_id = serializers.IntegerField(read_only=True)
|
||||
unique_serial = serializers.CharField(read_only=True)
|
||||
hardware_brand = serializers.CharField(read_only=True)
|
||||
hardware_model = serializers.CharField(read_only=True)
|
||||
software_brand = serializers.CharField(read_only=True)
|
||||
software_version = serializers.CharField(read_only=True)
|
||||
created = serializers.DateTimeField(read_only=True)
|
||||
revoked = serializers.BooleanField(read_only=True)
|
||||
initialized = serializers.DateTimeField(read_only=True)
|
||||
initialization_token = serializers.DateTimeField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Device
|
||||
fields = (
|
||||
'device_id', 'unique_serial', 'initialization_token', 'all_events', 'limit_events',
|
||||
'revoked', 'name', 'created', 'initialized', 'hardware_brand', 'hardware_model',
|
||||
'software_brand', 'software_version', 'security_profile'
|
||||
)
|
||||
|
||||
|
||||
class TeamInviteSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = TeamInvite
|
||||
|
||||
@@ -21,6 +21,7 @@ orga_router.register(r'webhooks', webhooks.WebHookViewSet)
|
||||
orga_router.register(r'seatingplans', organizer.SeatingPlanViewSet)
|
||||
orga_router.register(r'giftcards', organizer.GiftCardViewSet)
|
||||
orga_router.register(r'teams', organizer.TeamViewSet)
|
||||
orga_router.register(r'devices', organizer.DeviceViewSet)
|
||||
|
||||
team_router = routers.DefaultRouter()
|
||||
team_router.register(r'members', organizer.TeamMemberViewSet)
|
||||
@@ -44,7 +45,7 @@ event_router.register(r'checkinlists', checkin.CheckinListViewSet)
|
||||
event_router.register(r'cartpositions', cart.CartPositionViewSet)
|
||||
|
||||
checkinlist_router = routers.DefaultRouter()
|
||||
checkinlist_router.register(r'positions', checkin.CheckinListPositionViewSet)
|
||||
checkinlist_router.register(r'positions', checkin.CheckinListPositionViewSet, basename='checkinlistpos')
|
||||
|
||||
question_router = routers.DefaultRouter()
|
||||
question_router.register(r'options', item.QuestionOptionViewSet)
|
||||
|
||||
@@ -18,6 +18,7 @@ from pretix.api.serializers.item import QuestionSerializer
|
||||
from pretix.api.serializers.order import CheckinListOrderPositionSerializer
|
||||
from pretix.api.views import RichOrderingFilter
|
||||
from pretix.api.views.order import OrderPositionFilter
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
Checkin, CheckinList, Event, Order, OrderPosition,
|
||||
)
|
||||
@@ -87,73 +88,74 @@ class CheckinListViewSet(viewsets.ModelViewSet):
|
||||
|
||||
@action(detail=True, methods=['GET'])
|
||||
def status(self, *args, **kwargs):
|
||||
clist = self.get_object()
|
||||
cqs = Checkin.objects.filter(
|
||||
position__order__event=clist.event,
|
||||
position__order__status__in=[Order.STATUS_PAID] + ([Order.STATUS_PENDING] if clist.include_pending else []),
|
||||
list=clist
|
||||
)
|
||||
pqs = OrderPosition.objects.filter(
|
||||
order__event=clist.event,
|
||||
order__status__in=[Order.STATUS_PAID] + ([Order.STATUS_PENDING] if clist.include_pending else []),
|
||||
)
|
||||
if clist.subevent:
|
||||
pqs = pqs.filter(subevent=clist.subevent)
|
||||
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))
|
||||
with language(self.request.event.settings.locale):
|
||||
clist = self.get_object()
|
||||
cqs = Checkin.objects.filter(
|
||||
position__order__event=clist.event,
|
||||
position__order__status__in=[Order.STATUS_PAID] + ([Order.STATUS_PENDING] if clist.include_pending else []),
|
||||
list=clist
|
||||
)
|
||||
pqs = OrderPosition.objects.filter(
|
||||
order__event=clist.event,
|
||||
order__status__in=[Order.STATUS_PAID] + ([Order.STATUS_PENDING] if clist.include_pending else []),
|
||||
)
|
||||
if clist.subevent:
|
||||
pqs = pqs.filter(subevent=clist.subevent)
|
||||
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 = {
|
||||
'event': {
|
||||
'name': str(ev.name),
|
||||
},
|
||||
'checkin_count': cqs.count(),
|
||||
'position_count': pqs.count()
|
||||
}
|
||||
|
||||
op_by_item = {
|
||||
p['item']: p['cnt']
|
||||
for p in pqs.order_by().values('item').annotate(cnt=Count('id'))
|
||||
}
|
||||
op_by_variation = {
|
||||
p['variation']: p['cnt']
|
||||
for p in pqs.order_by().values('variation').annotate(cnt=Count('id'))
|
||||
}
|
||||
c_by_item = {
|
||||
p['position__item']: p['cnt']
|
||||
for p in cqs.order_by().values('position__item').annotate(cnt=Count('id'))
|
||||
}
|
||||
c_by_variation = {
|
||||
p['position__variation']: p['cnt']
|
||||
for p in cqs.order_by().values('position__variation').annotate(cnt=Count('id'))
|
||||
}
|
||||
|
||||
if not clist.all_products:
|
||||
items = clist.limit_products
|
||||
else:
|
||||
items = clist.event.items
|
||||
|
||||
response['items'] = []
|
||||
for item in items.order_by('category__position', 'position', 'pk').prefetch_related('variations'):
|
||||
i = {
|
||||
'id': item.pk,
|
||||
'name': str(item),
|
||||
'admission': item.admission,
|
||||
'checkin_count': c_by_item.get(item.pk, 0),
|
||||
'position_count': op_by_item.get(item.pk, 0),
|
||||
'variations': []
|
||||
ev = clist.subevent or clist.event
|
||||
response = {
|
||||
'event': {
|
||||
'name': str(ev.name),
|
||||
},
|
||||
'checkin_count': cqs.count(),
|
||||
'position_count': pqs.count()
|
||||
}
|
||||
for var in item.variations.all():
|
||||
i['variations'].append({
|
||||
'id': var.pk,
|
||||
'value': str(var),
|
||||
'checkin_count': c_by_variation.get(var.pk, 0),
|
||||
'position_count': op_by_variation.get(var.pk, 0),
|
||||
})
|
||||
response['items'].append(i)
|
||||
|
||||
return Response(response)
|
||||
op_by_item = {
|
||||
p['item']: p['cnt']
|
||||
for p in pqs.order_by().values('item').annotate(cnt=Count('id'))
|
||||
}
|
||||
op_by_variation = {
|
||||
p['variation']: p['cnt']
|
||||
for p in pqs.order_by().values('variation').annotate(cnt=Count('id'))
|
||||
}
|
||||
c_by_item = {
|
||||
p['position__item']: p['cnt']
|
||||
for p in cqs.order_by().values('position__item').annotate(cnt=Count('id'))
|
||||
}
|
||||
c_by_variation = {
|
||||
p['position__variation']: p['cnt']
|
||||
for p in cqs.order_by().values('position__variation').annotate(cnt=Count('id'))
|
||||
}
|
||||
|
||||
if not clist.all_products:
|
||||
items = clist.limit_products
|
||||
else:
|
||||
items = clist.event.items
|
||||
|
||||
response['items'] = []
|
||||
for item in items.order_by('category__position', 'position', 'pk').prefetch_related('variations'):
|
||||
i = {
|
||||
'id': item.pk,
|
||||
'name': str(item),
|
||||
'admission': item.admission,
|
||||
'checkin_count': c_by_item.get(item.pk, 0),
|
||||
'position_count': op_by_item.get(item.pk, 0),
|
||||
'variations': []
|
||||
}
|
||||
for var in item.variations.all():
|
||||
i['variations'].append({
|
||||
'id': var.pk,
|
||||
'value': str(var),
|
||||
'checkin_count': c_by_variation.get(var.pk, 0),
|
||||
'position_count': op_by_variation.get(var.pk, 0),
|
||||
})
|
||||
response['items'].append(i)
|
||||
|
||||
return Response(response)
|
||||
|
||||
|
||||
with scopes_disabled():
|
||||
|
||||
@@ -35,7 +35,7 @@ class DeviceSerializer(serializers.ModelSerializer):
|
||||
model = Device
|
||||
fields = [
|
||||
'organizer', 'device_id', 'unique_serial', 'api_token',
|
||||
'name'
|
||||
'name', 'security_profile'
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from pretix.api.serializers.event import (
|
||||
)
|
||||
from pretix.api.views import ConditionalListView
|
||||
from pretix.base.models import (
|
||||
CartPosition, Device, Event, ItemCategory, TaxRule, TeamAPIToken,
|
||||
CartPosition, Device, Event, TaxRule, TeamAPIToken,
|
||||
)
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.helpers.dicts import merge_dicts
|
||||
@@ -73,6 +73,7 @@ class EventViewSet(viewsets.ModelViewSet):
|
||||
queryset = Event.objects.none()
|
||||
lookup_field = 'slug'
|
||||
lookup_url_kwarg = 'event'
|
||||
lookup_value_regex = '[^/]+'
|
||||
permission_classes = (EventCRUDPermission,)
|
||||
filter_backends = (DjangoFilterBackend, filters.OrderingFilter)
|
||||
ordering = ('slug',)
|
||||
@@ -228,7 +229,7 @@ with scopes_disabled():
|
||||
|
||||
class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet):
|
||||
serializer_class = SubEventSerializer
|
||||
queryset = ItemCategory.objects.none()
|
||||
queryset = SubEvent.objects.none()
|
||||
write_permission = 'can_change_event_settings'
|
||||
filter_backends = (DjangoFilterBackend, filters.OrderingFilter)
|
||||
filterset_class = SubEventFilter
|
||||
|
||||
@@ -3,8 +3,9 @@ import logging
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.utils.translation import gettext as _
|
||||
from oauth2_provider.exceptions import OAuthToolkitError
|
||||
from oauth2_provider.exceptions import FatalClientError, OAuthToolkitError
|
||||
from oauth2_provider.forms import AllowForm
|
||||
from oauth2_provider.settings import oauth2_settings
|
||||
from oauth2_provider.views import (
|
||||
AuthorizationView as BaseAuthorizationView,
|
||||
RevokeTokenView as BaseRevokeTokenView, TokenView as BaseTokenView,
|
||||
@@ -24,9 +25,12 @@ class OAuthAllowForm(AllowForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
user = kwargs.pop('user')
|
||||
scope = kwargs.pop('scope')
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['organizers'].queryset = Organizer.objects.filter(
|
||||
pk__in=user.teams.values_list('organizer', flat=True))
|
||||
if scope == 'profile':
|
||||
del self.fields['organizers']
|
||||
|
||||
|
||||
class AuthorizationView(BaseAuthorizationView):
|
||||
@@ -36,6 +40,7 @@ class AuthorizationView(BaseAuthorizationView):
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs['user'] = self.request.user
|
||||
kwargs['scope'] = self.request.GET.get('scope')
|
||||
return kwargs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -43,8 +48,14 @@ class AuthorizationView(BaseAuthorizationView):
|
||||
ctx['settings'] = settings
|
||||
return ctx
|
||||
|
||||
def create_authorization_response(self, request, scopes, credentials, allow, organizers):
|
||||
credentials["organizers"] = organizers
|
||||
def validate_authorization_request(self, request):
|
||||
require_approval = request.GET.get("approval_prompt", oauth2_settings.REQUEST_APPROVAL_PROMPT)
|
||||
if require_approval != 'force' and request.GET.get('scope') != 'profile':
|
||||
raise FatalClientError('Combnination of require_approval and scope values not allowed.')
|
||||
return super().validate_authorization_request(request)
|
||||
|
||||
def create_authorization_response(self, request, scopes, credentials, allow, organizers=None):
|
||||
credentials["organizers"] = organizers or []
|
||||
return super().create_authorization_response(request, scopes, credentials, allow)
|
||||
|
||||
def form_valid(self, form):
|
||||
|
||||
@@ -228,6 +228,7 @@ class OrderViewSet(viewsets.ModelViewSet):
|
||||
@action(detail=True, methods=['POST'])
|
||||
def mark_paid(self, request, **kwargs):
|
||||
order = self.get_object()
|
||||
send_mail = request.data.get('send_email', True)
|
||||
|
||||
if order.status in (Order.STATUS_PENDING, Order.STATUS_EXPIRED):
|
||||
|
||||
@@ -269,6 +270,7 @@ class OrderViewSet(viewsets.ModelViewSet):
|
||||
try:
|
||||
p.confirm(auth=self.request.auth,
|
||||
user=self.request.user if request.user.is_authenticated else None,
|
||||
send_mail=send_mail,
|
||||
count_waitinglist=False)
|
||||
except Quota.QuotaExceededException as e:
|
||||
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
@@ -976,6 +978,7 @@ class PaymentViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
def confirm(self, request, **kwargs):
|
||||
payment = self.get_object()
|
||||
force = request.data.get('force', False)
|
||||
send_mail = request.data.get('send_email', True)
|
||||
|
||||
if payment.state not in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED):
|
||||
return Response({'detail': 'Invalid state of payment'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
@@ -984,6 +987,7 @@ class PaymentViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
payment.confirm(user=self.request.user if self.request.user.is_authenticated else None,
|
||||
auth=self.request.auth,
|
||||
count_waitinglist=False,
|
||||
send_mail=send_mail,
|
||||
force=force)
|
||||
except Quota.QuotaExceededException as e:
|
||||
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@@ -6,20 +6,22 @@ from django.shortcuts import get_object_or_404
|
||||
from django.utils.functional import cached_property
|
||||
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
|
||||
from django_scopes import scopes_disabled
|
||||
from rest_framework import filters, serializers, status, viewsets
|
||||
from rest_framework import filters, mixins, serializers, status, viewsets
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import MethodNotAllowed, PermissionDenied
|
||||
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from pretix.api.models import OAuthAccessToken
|
||||
from pretix.api.serializers.organizer import (
|
||||
GiftCardSerializer, OrganizerSerializer, SeatingPlanSerializer,
|
||||
TeamAPITokenSerializer, TeamInviteSerializer, TeamMemberSerializer,
|
||||
TeamSerializer,
|
||||
DeviceSerializer, GiftCardSerializer, OrganizerSerializer,
|
||||
SeatingPlanSerializer, TeamAPITokenSerializer, TeamInviteSerializer,
|
||||
TeamMemberSerializer, TeamSerializer,
|
||||
)
|
||||
from pretix.base.models import (
|
||||
GiftCard, Organizer, SeatingPlan, Team, TeamAPIToken, TeamInvite, User,
|
||||
Device, GiftCard, Organizer, SeatingPlan, Team, TeamAPIToken, TeamInvite,
|
||||
User,
|
||||
)
|
||||
from pretix.helpers.dicts import merge_dicts
|
||||
|
||||
@@ -29,6 +31,7 @@ class OrganizerViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = Organizer.objects.none()
|
||||
lookup_field = 'slug'
|
||||
lookup_url_kwarg = 'organizer'
|
||||
lookup_value_regex = '[^/]+'
|
||||
filter_backends = (filters.OrderingFilter,)
|
||||
ordering = ('slug',)
|
||||
ordering_fields = ('name', 'slug')
|
||||
@@ -352,3 +355,44 @@ class TeamAPITokenViewSet(CreateModelMixin, DestroyModelMixin, viewsets.ReadOnly
|
||||
serializer = self.get_serializer_class()(instance)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK, headers=headers)
|
||||
|
||||
|
||||
class DeviceViewSet(mixins.CreateModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
GenericViewSet):
|
||||
serializer_class = DeviceSerializer
|
||||
queryset = Device.objects.none()
|
||||
permission = 'can_change_organizer_settings'
|
||||
write_permission = 'can_change_organizer_settings'
|
||||
lookup_field = 'device_id'
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.organizer.devices.order_by('pk')
|
||||
|
||||
def get_serializer_context(self):
|
||||
ctx = super().get_serializer_context()
|
||||
ctx['organizer'] = self.request.organizer
|
||||
return ctx
|
||||
|
||||
@transaction.atomic()
|
||||
def perform_create(self, serializer):
|
||||
inst = serializer.save(organizer=self.request.organizer)
|
||||
inst.log_action(
|
||||
'pretix.device.created',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data=merge_dicts(self.request.data, {'id': inst.pk})
|
||||
)
|
||||
|
||||
@transaction.atomic()
|
||||
def perform_update(self, serializer):
|
||||
inst = serializer.save()
|
||||
inst.log_action(
|
||||
'pretix.device.changed',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data=self.request.data
|
||||
)
|
||||
return inst
|
||||
|
||||
@@ -3,14 +3,18 @@ from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from pretix.api.auth.permission import ProfilePermission
|
||||
|
||||
|
||||
class MeView(APIView):
|
||||
authentication_classes = (SessionAuthentication, OAuth2Authentication)
|
||||
permission_classes = (ProfilePermission,)
|
||||
|
||||
def get(self, request, format=None):
|
||||
return Response({
|
||||
'email': request.user.email,
|
||||
'fullname': request.user.fullname,
|
||||
'locale': request.user.locale,
|
||||
'is_staff': request.user.is_staff,
|
||||
'timezone': request.user.timezone
|
||||
})
|
||||
|
||||
@@ -85,6 +85,8 @@ class ParametrizedOrderWebhookEvent(WebhookEvent):
|
||||
|
||||
def build_payload(self, logentry: LogEntry):
|
||||
order = logentry.content_object
|
||||
if not order:
|
||||
return None
|
||||
|
||||
return {
|
||||
'notification_id': logentry.pk,
|
||||
@@ -99,6 +101,8 @@ class ParametrizedOrderPositionWebhookEvent(ParametrizedOrderWebhookEvent):
|
||||
|
||||
def build_payload(self, logentry: LogEntry):
|
||||
d = super().build_payload(logentry)
|
||||
if d is None:
|
||||
return None
|
||||
d['orderposition_id'] = logentry.parsed_data.get('position')
|
||||
d['orderposition_positionid'] = logentry.parsed_data.get('positionid')
|
||||
d['checkin_list'] = logentry.parsed_data.get('list')
|
||||
@@ -218,6 +222,10 @@ def send_webhook(self, logentry_id: int, action_type: str, webhook_id: int):
|
||||
return # Ignore, e.g. plugin not installed
|
||||
|
||||
payload = event_type.build_payload(logentry)
|
||||
if payload is None:
|
||||
# Content object deleted?
|
||||
return
|
||||
|
||||
t = time.time()
|
||||
|
||||
try:
|
||||
|
||||
@@ -98,7 +98,10 @@ class BaseAuthBackend:
|
||||
|
||||
class NativeAuthBackend(BaseAuthBackend):
|
||||
identifier = 'native'
|
||||
verbose_name = _('pretix User')
|
||||
|
||||
@property
|
||||
def verbose_name(self):
|
||||
return _('{system} User').format(system=settings.PRETIX_INSTANCE_NAME)
|
||||
|
||||
@property
|
||||
def login_form_fields(self) -> dict:
|
||||
|
||||
@@ -114,7 +114,7 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
|
||||
'site_url': settings.SITE_URL,
|
||||
'body': body_md,
|
||||
'subject': str(subject),
|
||||
'color': '#8E44B3',
|
||||
'color': settings.PRETIX_PRIMARY_COLOR,
|
||||
'rtl': get_language() in settings.LANGUAGES_RTL
|
||||
}
|
||||
if self.event:
|
||||
@@ -222,6 +222,7 @@ class SimpleFunctionalMailTextPlaceholder(BaseMailTextPlaceholder):
|
||||
def get_available_placeholders(event, base_parameters):
|
||||
if 'order' in base_parameters:
|
||||
base_parameters.append('invoice_address')
|
||||
base_parameters.append('position_or_address')
|
||||
params = {}
|
||||
for r, val in register_mail_placeholders.send(sender=event):
|
||||
if not isinstance(val, (list, tuple)):
|
||||
@@ -240,7 +241,9 @@ def get_email_context(**kwargs):
|
||||
try:
|
||||
kwargs['invoice_address'] = kwargs['order'].invoice_address
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
kwargs['invoice_address'] = InvoiceAddress()
|
||||
kwargs['invoice_address'] = InvoiceAddress(order=kwargs['order'])
|
||||
finally:
|
||||
kwargs.setdefault("position_or_address", kwargs['invoice_address'])
|
||||
ctx = {}
|
||||
for r, val in register_mail_placeholders.send(sender=event):
|
||||
if not isinstance(val, (list, tuple)):
|
||||
@@ -268,7 +271,8 @@ def get_best_name(position_or_address, parts=False):
|
||||
if isinstance(position_or_address, InvoiceAddress):
|
||||
if position_or_address.name:
|
||||
return position_or_address.name_parts if parts else position_or_address.name
|
||||
position_or_address = position_or_address.order.positions.exclude(attendee_name_cached="").exclude(attendee_name_cached__isnull=True).first()
|
||||
elif position_or_address.order:
|
||||
position_or_address = position_or_address.order.positions.exclude(attendee_name_cached="").exclude(attendee_name_cached__isnull=True).first()
|
||||
|
||||
if isinstance(position_or_address, OrderPosition):
|
||||
if position_or_address.attendee_name:
|
||||
|
||||
@@ -388,6 +388,11 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
pgettext('address', 'State'),
|
||||
_('Voucher'),
|
||||
_('Pseudonymization ID'),
|
||||
_('Seat ID'),
|
||||
_('Seat name'),
|
||||
_('Seat zone'),
|
||||
_('Seat row'),
|
||||
_('Seat number'),
|
||||
]
|
||||
|
||||
questions = list(Question.objects.filter(event__in=self.events))
|
||||
@@ -471,6 +476,18 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
op.voucher.code if op.voucher else '',
|
||||
op.pseudonymization_id,
|
||||
]
|
||||
|
||||
if op.seat:
|
||||
row += [
|
||||
op.seat.seat_guid,
|
||||
str(op.seat),
|
||||
op.seat.zone_name,
|
||||
op.seat.row_name,
|
||||
op.seat.seat_number,
|
||||
]
|
||||
else:
|
||||
row += ['', '', '', '', '']
|
||||
|
||||
acache = {}
|
||||
for a in op.answers.all():
|
||||
# We do not want to localize Date, Time and Datetime question answers, as those can lead
|
||||
@@ -645,11 +662,13 @@ class GiftcardRedemptionListExporter(ListExporter):
|
||||
def iterate_list(self, form_data):
|
||||
payments = OrderPayment.objects.filter(
|
||||
order__event__in=self.events,
|
||||
provider='giftcard'
|
||||
provider='giftcard',
|
||||
state__in=(OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_REFUNDED),
|
||||
).order_by('created')
|
||||
refunds = OrderRefund.objects.filter(
|
||||
order__event__in=self.events,
|
||||
provider='giftcard'
|
||||
provider='giftcard',
|
||||
state=OrderRefund.REFUND_STATE_DONE
|
||||
).order_by('created')
|
||||
|
||||
objs = sorted(list(payments) + list(refunds), key=lambda o: (o.order.code, o.created))
|
||||
|
||||
@@ -36,8 +36,8 @@ from pretix.base.i18n import language
|
||||
from pretix.base.models import InvoiceAddress, Question, QuestionOption
|
||||
from pretix.base.models.tax import EU_COUNTRIES, cc_to_vat_prefix
|
||||
from pretix.base.settings import (
|
||||
COUNTRIES_WITH_STATE_IN_ADDRESS, PERSON_NAME_SCHEMES,
|
||||
PERSON_NAME_TITLE_GROUPS,
|
||||
COUNTRIES_WITH_STATE_IN_ADDRESS, PERSON_NAME_SALUTATIONS,
|
||||
PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS,
|
||||
)
|
||||
from pretix.base.templatetags.rich_text import rich_text
|
||||
from pretix.control.forms import ExtFileField, SplitDateTimeField
|
||||
@@ -49,7 +49,7 @@ from pretix.presale.signals import question_form_fields
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
REQUIRED_NAME_PARTS = ['given_name', 'family_name', 'full_name']
|
||||
REQUIRED_NAME_PARTS = ['salutation', 'given_name', 'family_name', 'full_name']
|
||||
|
||||
|
||||
class NamePartsWidget(forms.MultiWidget):
|
||||
@@ -73,6 +73,8 @@ class NamePartsWidget(forms.MultiWidget):
|
||||
a['data-fname'] = fname
|
||||
if fname == 'title' and self.titles:
|
||||
widgets.append(Select(attrs=a, choices=[('', '')] + [(d, d) for d in self.titles[1]]))
|
||||
elif fname == 'salutation':
|
||||
widgets.append(Select(attrs=a, choices=[('', '---')] + [(s, s) for s in PERSON_NAME_SALUTATIONS]))
|
||||
else:
|
||||
widgets.append(self.widget(attrs=a))
|
||||
super().__init__(widgets, attrs)
|
||||
@@ -162,12 +164,18 @@ class NamePartsFormField(forms.MultiValueField):
|
||||
**d,
|
||||
choices=[('', '')] + [(d, d) for d in self.scheme_titles[1]]
|
||||
)
|
||||
field.part_name = fname
|
||||
fields.append(field)
|
||||
|
||||
elif fname == 'salutation':
|
||||
d = dict(defaults)
|
||||
d.pop('max_length', None)
|
||||
field = forms.ChoiceField(
|
||||
**d,
|
||||
choices=[('', '---')] + [(s, s) for s in PERSON_NAME_SALUTATIONS]
|
||||
)
|
||||
else:
|
||||
field = forms.CharField(**defaults)
|
||||
field.part_name = fname
|
||||
fields.append(field)
|
||||
field.part_name = fname
|
||||
fields.append(field)
|
||||
super().__init__(
|
||||
fields=fields, require_all_fields=False, *args, **kwargs
|
||||
)
|
||||
|
||||
@@ -396,13 +396,13 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
p_str = (
|
||||
shorten(self.invoice.event.name) + '\n' +
|
||||
pgettext('invoice', '{from_date}\nuntil {to_date}').format(
|
||||
from_date=self.invoice.event.get_date_from_display(),
|
||||
to_date=self.invoice.event.get_date_to_display()
|
||||
from_date=self.invoice.event.get_date_from_display(show_times=False),
|
||||
to_date=self.invoice.event.get_date_to_display(show_times=False)
|
||||
)
|
||||
)
|
||||
else:
|
||||
p_str = (
|
||||
shorten(self.invoice.event.name) + '\n' + self.invoice.event.get_date_from_display()
|
||||
shorten(self.invoice.event.name) + '\n' + self.invoice.event.get_date_from_display(show_times=False)
|
||||
)
|
||||
else:
|
||||
p_str = shorten(self.invoice.event.name)
|
||||
|
||||
18
src/pretix/base/migrations/0163_device_security_profile.py
Normal file
18
src/pretix/base/migrations/0163_device_security_profile.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.0.9 on 2020-10-13 08:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0162_remove_seat_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='security_profile',
|
||||
field=models.CharField(default='full', max_length=190, null=True),
|
||||
),
|
||||
]
|
||||
@@ -6,6 +6,7 @@ from django.utils.crypto import get_random_string
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_scopes import ScopedManager, scopes_disabled
|
||||
|
||||
from pretix.api.auth.devicesecurity import DEVICE_SECURITY_PROFILES
|
||||
from pretix.base.models import LoggedModel
|
||||
|
||||
|
||||
@@ -74,6 +75,13 @@ class Device(LoggedModel):
|
||||
max_length=190,
|
||||
null=True, blank=True
|
||||
)
|
||||
security_profile = models.CharField(
|
||||
max_length=190,
|
||||
choices=[(k, v.verbose_name) for k, v in DEVICE_SECURITY_PROFILES.items()],
|
||||
default='full',
|
||||
null=True,
|
||||
blank=False
|
||||
)
|
||||
|
||||
objects = ScopedManager(organizer='organizer')
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ from django.core.files.storage import default_storage
|
||||
from django.core.mail import get_connection
|
||||
from django.core.validators import RegexValidator
|
||||
from django.db import models
|
||||
from django.db.models import Exists, OuterRef, Prefetch, Q, Subquery
|
||||
from django.db.models import Exists, OuterRef, Prefetch, Q, Subquery, Value
|
||||
from django.template.defaultfilters import date as _date
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.formats import date_format
|
||||
@@ -89,7 +89,7 @@ class EventMixin:
|
||||
self.date_from.astimezone(tz), "TIME_FORMAT"
|
||||
)
|
||||
|
||||
def get_date_to_display(self, tz=None, short=False) -> str:
|
||||
def get_date_to_display(self, tz=None, show_times=True, short=False) -> str:
|
||||
"""
|
||||
Returns a formatted string containing the start date of the event with respect
|
||||
to the current locale and to the ``show_times`` setting. Returns an empty string
|
||||
@@ -100,14 +100,14 @@ class EventMixin:
|
||||
return ""
|
||||
return _date(
|
||||
self.date_to.astimezone(tz),
|
||||
("SHORT_" if short else "") + ("DATETIME_FORMAT" if self.settings.show_times else "DATE_FORMAT")
|
||||
("SHORT_" if short else "") + ("DATETIME_FORMAT" if self.settings.show_times and show_times else "DATE_FORMAT")
|
||||
)
|
||||
|
||||
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.
|
||||
of the event with respect to the current locale and to the ``show_date_to``
|
||||
setting. Times are not shown.
|
||||
"""
|
||||
tz = tz or self.timezone
|
||||
if (not self.settings.show_date_to and not force_show_end) or not self.date_to:
|
||||
@@ -189,7 +189,9 @@ class EventMixin:
|
||||
).order_by().values_list('quotas__pk').annotate(
|
||||
items=GroupConcat('pk', delimiter=',')
|
||||
).values('items')
|
||||
return qs.prefetch_related(
|
||||
return qs.annotate(
|
||||
has_paid_item=Exists(Item.objects.filter(event_id=OuterRef(cls._event_id), default_price__gt=0))
|
||||
).prefetch_related(
|
||||
Prefetch(
|
||||
'quotas',
|
||||
to_attr='active_quotas',
|
||||
@@ -280,6 +282,7 @@ class Event(EventMixin, LoggedModel):
|
||||
"""
|
||||
|
||||
settings_namespace = 'event'
|
||||
_event_id = 'pk'
|
||||
CURRENCY_CHOICES = [(c.alpha_3, c.alpha_3 + " - " + c.name) for c in settings.CURRENCIES]
|
||||
organizer = models.ForeignKey(Organizer, related_name="events", on_delete=models.PROTECT)
|
||||
testmode = models.BooleanField(default=False)
|
||||
@@ -786,7 +789,12 @@ class Event(EventMixin, LoggedModel):
|
||||
'name_ascending': ('name', 'date_from'),
|
||||
'name_descending': ('-name', 'date_from'),
|
||||
}[ordering]
|
||||
subevs = queryset.filter(
|
||||
subevs = queryset.annotate(
|
||||
has_paid_item=Value(
|
||||
self.cache.get_or_set('has_paid_item', lambda: self.items.filter(default_price__gt=0).exists(), 3600),
|
||||
output_field=models.BooleanField()
|
||||
)
|
||||
).filter(
|
||||
Q(active=True) & Q(is_public=True) & (
|
||||
Q(Q(date_to__isnull=True) & Q(date_from__gte=now() - timedelta(hours=24)))
|
||||
| Q(date_to__gte=now() - timedelta(hours=24))
|
||||
@@ -980,6 +988,7 @@ class SubEvent(EventMixin, LoggedModel):
|
||||
:type location: str
|
||||
"""
|
||||
|
||||
_event_id = 'event_id'
|
||||
event = models.ForeignKey(Event, related_name="subevents", on_delete=models.PROTECT)
|
||||
active = models.BooleanField(default=False, verbose_name=_("Active"),
|
||||
help_text=_("Only with this checkbox enabled, this date is visible in the "
|
||||
|
||||
@@ -1140,6 +1140,8 @@ class Question(LoggedModel):
|
||||
return None
|
||||
|
||||
if self.type == Question.TYPE_CHOICE:
|
||||
if isinstance(answer, QuestionOption):
|
||||
return answer
|
||||
q = Q(identifier=answer)
|
||||
if isinstance(answer, int) or answer.isdigit():
|
||||
q |= Q(pk=answer)
|
||||
@@ -1154,6 +1156,8 @@ class Question(LoggedModel):
|
||||
Q(identifier__in=answer.split(","))
|
||||
))
|
||||
llen = len(answer.split(','))
|
||||
elif all(isinstance(o, QuestionOption) for o in answer):
|
||||
return o
|
||||
else:
|
||||
l_ = list(self.options.filter(
|
||||
Q(pk__in=[a for a in answer if isinstance(a, int) or a.isdigit()]) |
|
||||
|
||||
@@ -174,7 +174,7 @@ class TaxRule(LoggedModel):
|
||||
return Decimal(self.rate)
|
||||
|
||||
def tax(self, base_price, base_price_is='auto', currency=None, override_tax_rate=None, invoice_address=None,
|
||||
subtract_from_gross=Decimal('0.00')):
|
||||
subtract_from_gross=Decimal('0.00'), gross_price_is_tax_rate: Decimal = None):
|
||||
from .event import Event
|
||||
try:
|
||||
currency = currency or self.event.currency
|
||||
@@ -186,7 +186,9 @@ class TaxRule(LoggedModel):
|
||||
rate = override_tax_rate
|
||||
elif invoice_address:
|
||||
adjust_rate = self.tax_rate_for(invoice_address)
|
||||
if adjust_rate != rate:
|
||||
if adjust_rate == gross_price_is_tax_rate and base_price_is == 'gross':
|
||||
rate = adjust_rate
|
||||
elif adjust_rate != rate:
|
||||
normal_price = self.tax(base_price, base_price_is, currency, subtract_from_gross=subtract_from_gross)
|
||||
base_price = normal_price.net
|
||||
base_price_is = 'net'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from decimal import Decimal, DecimalException
|
||||
|
||||
import pycountry
|
||||
@@ -12,11 +13,13 @@ from django.utils.translation import (
|
||||
)
|
||||
from django_countries import countries
|
||||
from django_countries.fields import Country
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base.channels import get_all_sales_channels
|
||||
from pretix.base.forms.questions import guess_country
|
||||
from pretix.base.models import (
|
||||
ItemVariation, OrderPosition, QuestionAnswer, QuestionOption, Seat,
|
||||
ItemVariation, OrderPosition, Question, QuestionAnswer, QuestionOption,
|
||||
Seat,
|
||||
)
|
||||
from pretix.base.services.pricing import get_price
|
||||
from pretix.base.settings import (
|
||||
@@ -417,7 +420,7 @@ class AttendeeStreet(ImportColumn):
|
||||
return _('Attendee address') + ': ' + _('Address')
|
||||
|
||||
def assign(self, value, order, position, invoice_address, **kwargs):
|
||||
position.address = value or ''
|
||||
position.street = value or ''
|
||||
|
||||
|
||||
class AttendeeZip(ImportColumn):
|
||||
@@ -528,7 +531,7 @@ class Secret(ImportColumn):
|
||||
super().__init__(*args)
|
||||
|
||||
def clean(self, value, previous_values):
|
||||
if value and (value in self._cached or OrderPosition.all.filter(order__event=self.event, secret=value).exists()):
|
||||
if value and (value in self._cached or OrderPosition.all.filter(order__event__organizer=self.event.organizer, secret=value).exists()):
|
||||
raise ValidationError(
|
||||
_('You cannot assign a position secret that already exists.')
|
||||
)
|
||||
@@ -626,6 +629,22 @@ class Comment(ImportColumn):
|
||||
class QuestionColumn(ImportColumn):
|
||||
def __init__(self, event, q):
|
||||
self.q = q
|
||||
self.option_resolve_cache = defaultdict(set)
|
||||
|
||||
for opt in q.options.all():
|
||||
self.option_resolve_cache[str(opt.id)].add(opt)
|
||||
self.option_resolve_cache[opt.identifier].add(opt)
|
||||
|
||||
if isinstance(opt.answer, LazyI18nString):
|
||||
|
||||
if isinstance(opt.answer.data, dict):
|
||||
for v in opt.answer.data.values():
|
||||
self.option_resolve_cache[v.strip()].add(opt)
|
||||
else:
|
||||
self.option_resolve_cache[opt.answer.strip()].add(opt)
|
||||
|
||||
else:
|
||||
self.option_resolve_cache[opt.answer.strip()].add(opt)
|
||||
super().__init__(event)
|
||||
|
||||
@property
|
||||
@@ -638,7 +657,23 @@ class QuestionColumn(ImportColumn):
|
||||
|
||||
def clean(self, value, previous_values):
|
||||
if value:
|
||||
return self.q.clean_answer(value)
|
||||
if self.q.type == Question.TYPE_CHOICE:
|
||||
if value not in self.option_resolve_cache:
|
||||
raise ValidationError(_('Invalid option selected.'))
|
||||
if len(self.option_resolve_cache[value]) > 1:
|
||||
raise ValidationError(_('Ambigous option selected.'))
|
||||
return list(self.option_resolve_cache[value])[0]
|
||||
|
||||
elif self.q.type == Question.TYPE_CHOICE_MULTIPLE:
|
||||
values = value.split(',')
|
||||
if any(v.strip() not in self.option_resolve_cache for v in values):
|
||||
raise ValidationError(_('Invalid option selected.'))
|
||||
if any(len(self.option_resolve_cache[v.strip()]) > 1 for v in values):
|
||||
raise ValidationError(_('Ambigous option selected.'))
|
||||
return [list(self.option_resolve_cache[v.strip()])[0] for v in values]
|
||||
|
||||
else:
|
||||
return self.q.clean_answer(value)
|
||||
|
||||
def assign(self, value, order, position, invoice_address, **kwargs):
|
||||
if value:
|
||||
@@ -702,7 +737,7 @@ def get_all_columns(event):
|
||||
SeatColumn(event),
|
||||
Comment(event)
|
||||
]
|
||||
for q in event.questions.exclude(type='F'):
|
||||
for q in event.questions.prefetch_related('options').exclude(type=Question.TYPE_FILE):
|
||||
default.append(QuestionColumn(event, q))
|
||||
|
||||
for recv, resp in order_import_columns.send(sender=event):
|
||||
|
||||
@@ -1134,7 +1134,7 @@ class GiftCardPayment(BasePaymentProvider):
|
||||
cart['raw']
|
||||
)
|
||||
total += sum([f.value for f in fees])
|
||||
remainder = total - gc.value
|
||||
remainder = total
|
||||
if remainder > Decimal('0.00'):
|
||||
del cs['payment']
|
||||
messages.success(request, _("Your gift card has been applied, but {} still need to be paid. Please select a payment method.").format(
|
||||
|
||||
@@ -66,6 +66,7 @@ error_messages = {
|
||||
"%(min)s items of it."),
|
||||
'not_started': _('The presale period for this event has not yet started.'),
|
||||
'ended': _('The presale period for this event has ended.'),
|
||||
'payment_ended': _('All payments for this event need to be confirmed already, so no new orders can be created.'),
|
||||
'some_subevent_not_started': _('The presale period for this event has not yet started. The affected positions '
|
||||
'have been removed from your cart.'),
|
||||
'some_subevent_ended': _('The presale period for one of the events in your cart has ended. The affected '
|
||||
@@ -169,7 +170,7 @@ class CartManager:
|
||||
time(hour=23, minute=59, second=59)
|
||||
), self.event.timezone)
|
||||
if term_last < self.now_dt:
|
||||
raise CartError(error_messages['ended'])
|
||||
raise CartError(error_messages['payment_ended'])
|
||||
|
||||
def _extend_expiry_of_valid_existing_positions(self):
|
||||
# Extend this user's cart session to ensure all items in the cart expire at the same time
|
||||
@@ -304,7 +305,7 @@ class CartManager:
|
||||
time(hour=23, minute=59, second=59)
|
||||
), self.event.timezone)
|
||||
if term_last < self.now_dt:
|
||||
raise CartError(error_messages['ended'])
|
||||
raise CartError(error_messages['payment_ended'])
|
||||
|
||||
if isinstance(op, self.AddOperation):
|
||||
if op.item.category and op.item.category.is_addon and not (op.addon_to and op.addon_to != 'FAKE'):
|
||||
@@ -1085,16 +1086,14 @@ def get_fees(event, request, total, invoice_address, provider, positions):
|
||||
if cs.get('gift_cards'):
|
||||
gcs = cs['gift_cards']
|
||||
gc_qs = event.organizer.accepted_gift_cards.filter(pk__in=cs.get('gift_cards'), currency=event.currency)
|
||||
summed = 0
|
||||
for gc in gc_qs:
|
||||
if gc.testmode != event.testmode:
|
||||
gcs.remove(gc.pk)
|
||||
continue
|
||||
fval = Decimal(gc.value) # TODO: don't require an extra query
|
||||
fval = min(fval, total - summed)
|
||||
fval = min(fval, total)
|
||||
if fval > 0:
|
||||
total -= fval
|
||||
summed += fval
|
||||
fees.append(OrderFee(
|
||||
fee_type=OrderFee.FEE_TYPE_GIFTCARD,
|
||||
internal_type='giftcard',
|
||||
|
||||
@@ -7,7 +7,7 @@ import ssl
|
||||
import warnings
|
||||
from email.mime.image import MIMEImage
|
||||
from email.utils import formataddr
|
||||
from typing import Any, Dict, List, Union
|
||||
from typing import Any, Dict, List, Sequence, Union
|
||||
from urllib.parse import urljoin, urlparse
|
||||
|
||||
import cssutils
|
||||
@@ -27,7 +27,7 @@ from i18nfield.strings import LazyI18nString
|
||||
from pretix.base.email import ClassicMailRenderer
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
Event, Invoice, InvoiceAddress, Order, OrderPosition, User,
|
||||
CachedFile, Event, Invoice, InvoiceAddress, Order, OrderPosition, User,
|
||||
)
|
||||
from pretix.base.services.invoices import invoice_pdf_task
|
||||
from pretix.base.services.tasks import TransactionAwareTask
|
||||
@@ -52,10 +52,11 @@ class SendMailException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def mail(email: str, subject: str, template: Union[str, LazyI18nString],
|
||||
context: Dict[str, Any]=None, event: Event=None, locale: str=None,
|
||||
order: Order=None, position: OrderPosition=None, headers: dict=None, sender: str=None,
|
||||
invoices: list=None, attach_tickets=False, auto_email=True, user=None, attach_ical=False):
|
||||
def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, LazyI18nString],
|
||||
context: Dict[str, Any] = None, event: Event = None, locale: str = None,
|
||||
order: Order = None, position: OrderPosition = None, headers: dict = None, sender: str = None,
|
||||
invoices: Sequence = None, attach_tickets=False, auto_email=True, user=None, attach_ical=False,
|
||||
attach_cached_files: Sequence = None):
|
||||
"""
|
||||
Sends out an email to a user. The mail will be sent synchronously or asynchronously depending on the installation.
|
||||
|
||||
@@ -96,6 +97,8 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
|
||||
|
||||
:param user: The user this email is sent to
|
||||
|
||||
:param attach_cached_files: A list of cached file to attach to this email.
|
||||
|
||||
:raises MailOrderException: on obvious, immediate failures. Not raising an exception does not necessarily mean
|
||||
that the email has been sent, just that it has been queued by the email backend.
|
||||
"""
|
||||
@@ -214,7 +217,7 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
|
||||
body_html = None
|
||||
|
||||
send_task = mail_send_task.si(
|
||||
to=[email],
|
||||
to=[email] if isinstance(email, str) else list(email),
|
||||
bcc=bcc,
|
||||
subject=subject,
|
||||
body=body_plain,
|
||||
@@ -227,7 +230,8 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
|
||||
position=position.pk if position else None,
|
||||
attach_tickets=attach_tickets,
|
||||
attach_ical=attach_ical,
|
||||
user=user.pk if user else None
|
||||
user=user.pk if user else None,
|
||||
attach_cached_files=[cf.id for cf in attach_cached_files] if attach_cached_files else [],
|
||||
)
|
||||
|
||||
if invoices:
|
||||
@@ -255,9 +259,9 @@ class CustomEmail(EmailMultiAlternatives):
|
||||
|
||||
@app.task(base=TransactionAwareTask, bind=True, acks_late=True)
|
||||
def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: str, sender: str,
|
||||
event: int=None, position: int=None, headers: dict=None, bcc: List[str]=None,
|
||||
invoices: List[int]=None, order: int=None, attach_tickets=False, user=None,
|
||||
attach_ical=False) -> bool:
|
||||
event: int = None, position: int = None, headers: dict = None, bcc: List[str] = None,
|
||||
invoices: List[int] = None, order: int = None, attach_tickets=False, user=None,
|
||||
attach_ical=False, attach_cached_files: List[int] = None) -> bool:
|
||||
email = CustomEmail(subject, body, sender, to=to, bcc=bcc, headers=headers)
|
||||
if html is not None:
|
||||
html_message = SafeMIMEMultipart(_subtype='related', encoding=settings.DEFAULT_CHARSET)
|
||||
@@ -349,6 +353,19 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
|
||||
logger.exception('Could not attach invoice to email')
|
||||
pass
|
||||
|
||||
if attach_cached_files:
|
||||
for cf in CachedFile.objects.filter(id__in=attach_cached_files):
|
||||
if cf.file:
|
||||
try:
|
||||
email.attach(
|
||||
cf.filename,
|
||||
cf.file.file.read(),
|
||||
cf.type,
|
||||
)
|
||||
except:
|
||||
logger.exception('Could not attach file to email')
|
||||
pass
|
||||
|
||||
email = global_email_filter.send_chained(event, 'message', message=email, user=user, order=order)
|
||||
|
||||
try:
|
||||
|
||||
@@ -91,7 +91,7 @@ def send_notification_mail(notification: Notification, user: User):
|
||||
ctx = {
|
||||
'site': settings.PRETIX_INSTANCE_NAME,
|
||||
'site_url': settings.SITE_URL,
|
||||
'color': '#8E44B3',
|
||||
'color': settings.PRETIX_PRIMARY_COLOR,
|
||||
'notification': notification,
|
||||
'settings_url': build_absolute_uri(
|
||||
'control:user.settings.notifications',
|
||||
|
||||
@@ -103,7 +103,7 @@ def import_orders(event: Event, fileid: str, settings: dict, locale: str, user)
|
||||
order._address.name_parts = {'_scheme': event.settings.name_scheme}
|
||||
orders.append(order)
|
||||
|
||||
position = OrderPosition()
|
||||
position = OrderPosition(positionid=len(order._positions) + 1)
|
||||
position.attendee_name_parts = {'_scheme': event.settings.name_scheme}
|
||||
position.meta_info = {}
|
||||
order._positions.append(position)
|
||||
|
||||
@@ -618,8 +618,10 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
|
||||
except ItemBundle.MultipleObjectsReturned:
|
||||
raise OrderError("Invalid product configuration (duplicate bundle)")
|
||||
price = get_price(cp.item, cp.variation, cp.voucher, bprice, cp.subevent, custom_price_is_net=False,
|
||||
custom_price_is_tax_rate=cp.override_tax_rate,
|
||||
invoice_address=address, force_custom_price=True, max_discount=max_discount)
|
||||
pbv = get_price(cp.item, cp.variation, None, bprice, cp.subevent, custom_price_is_net=False,
|
||||
custom_price_is_tax_rate=cp.override_tax_rate,
|
||||
invoice_address=address, force_custom_price=True, max_discount=max_discount)
|
||||
changed_prices[cp.pk] = bprice
|
||||
else:
|
||||
@@ -631,10 +633,10 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
|
||||
|
||||
price = get_price(cp.item, cp.variation, cp.voucher, cp.price, cp.subevent, custom_price_is_net=False,
|
||||
addon_to=cp.addon_to, invoice_address=address, bundled_sum=bundled_sum,
|
||||
max_discount=max_discount)
|
||||
max_discount=max_discount, custom_price_is_tax_rate=cp.override_tax_rate)
|
||||
pbv = get_price(cp.item, cp.variation, None, cp.price, cp.subevent, custom_price_is_net=False,
|
||||
addon_to=cp.addon_to, invoice_address=address, bundled_sum=bundled_sum,
|
||||
max_discount=max_discount)
|
||||
max_discount=max_discount, custom_price_is_tax_rate=cp.override_tax_rate)
|
||||
|
||||
if max_discount is not None:
|
||||
v_budget[cp.voucher] = v_budget[cp.voucher] + current_discount - (pbv.gross - price.gross)
|
||||
@@ -1051,7 +1053,7 @@ def send_download_reminders(sender, **kwargs):
|
||||
download_reminder_sent=False,
|
||||
datetime__lte=now() - timedelta(hours=2),
|
||||
first_date__gte=today,
|
||||
).only('pk', 'event_id').order_by('event_id')
|
||||
).only('pk', 'event_id', 'sales_channel').order_by('event_id')
|
||||
event_id = None
|
||||
days = None
|
||||
event = None
|
||||
@@ -1882,7 +1884,7 @@ class OrderChangeManager:
|
||||
def _reissue_invoice(self):
|
||||
i = self.order.invoices.filter(is_cancellation=False).last()
|
||||
if self.reissue_invoice and self._invoice_dirty:
|
||||
if i:
|
||||
if i and not i.refered.exists():
|
||||
self._invoices.append(generate_cancellation(i))
|
||||
if invoice_qualified(self.order) and \
|
||||
(i or
|
||||
@@ -2205,8 +2207,11 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
|
||||
|
||||
|
||||
@receiver(order_paid, dispatch_uid="pretixbase_order_paid_giftcards")
|
||||
@receiver(order_changed, dispatch_uid="pretixbase_order_changed_giftcards")
|
||||
@transaction.atomic()
|
||||
def signal_listener_issue_giftcards(sender: Event, order: Order, **kwargs):
|
||||
if order.status != Order.STATUS_PAID:
|
||||
return
|
||||
any_giftcards = False
|
||||
for p in order.positions.all():
|
||||
if p.item.issue_giftcard:
|
||||
|
||||
@@ -11,6 +11,7 @@ from pretix.base.models.tax import TAXED_ZERO, TaxedPrice, TaxRule
|
||||
def get_price(item: Item, variation: ItemVariation = None,
|
||||
voucher: Voucher = None, custom_price: Decimal = None,
|
||||
subevent: SubEvent = None, custom_price_is_net: bool = False,
|
||||
custom_price_is_tax_rate: Decimal=None,
|
||||
addon_to: AbstractPosition = None, invoice_address: InvoiceAddress = None,
|
||||
force_custom_price: bool = False, bundled_sum: Decimal = Decimal('0.00'),
|
||||
max_discount: Decimal = None, tax_rule=None) -> TaxedPrice:
|
||||
@@ -66,7 +67,7 @@ def get_price(item: Item, variation: ItemVariation = None,
|
||||
price = tax_rule.tax(max(custom_price, price.net), base_price_is='net',
|
||||
invoice_address=invoice_address, subtract_from_gross=bundled_sum)
|
||||
else:
|
||||
price = tax_rule.tax(max(custom_price, price.gross), base_price_is='gross',
|
||||
price = tax_rule.tax(max(custom_price, price.gross), base_price_is='gross', gross_price_is_tax_rate=custom_price_is_tax_rate,
|
||||
invoice_address=invoice_address, subtract_from_gross=bundled_sum)
|
||||
else:
|
||||
price = tax_rule.tax(price, invoice_address=invoice_address, subtract_from_gross=bundled_sum)
|
||||
|
||||
@@ -54,7 +54,7 @@ def export(event: Event, shredders: List[str]) -> None:
|
||||
cf = CachedFile()
|
||||
cf.date = now()
|
||||
cf.filename = event.slug + '.zip'
|
||||
cf.type = 'application/pdf'
|
||||
cf.type = 'application/zip'
|
||||
cf.expires = now() + timedelta(hours=1)
|
||||
cf.save()
|
||||
cf.file.save(cachedfile_name(cf, cf.filename), rawfile)
|
||||
|
||||
@@ -668,8 +668,8 @@ DEFAULTS = {
|
||||
'type': str,
|
||||
'form_class': forms.ChoiceField,
|
||||
'serializer_class': serializers.ChoiceField,
|
||||
'serializer_kwargs': country_choice_kwargs,
|
||||
'form_kwargs': country_choice_kwargs,
|
||||
'serializer_kwargs': lambda: dict(**country_choice_kwargs()),
|
||||
'form_kwargs': lambda: dict(label=_('Country'), **country_choice_kwargs()),
|
||||
},
|
||||
'invoice_address_from_tax_id': {
|
||||
'default': '',
|
||||
@@ -1580,7 +1580,7 @@ Your {event} team"""))
|
||||
'type': bool
|
||||
},
|
||||
'primary_color': {
|
||||
'default': '#8E44B3',
|
||||
'default': settings.PRETIX_PRIMARY_COLOR,
|
||||
'type': str,
|
||||
},
|
||||
'theme_color_success': {
|
||||
@@ -1825,6 +1825,15 @@ Your {event} team"""))
|
||||
'seating_distance_within_row': {
|
||||
'default': 'False',
|
||||
'type': bool
|
||||
},
|
||||
'checkout_show_copy_answers_button': {
|
||||
'default': 'True',
|
||||
'type': bool,
|
||||
'form_class': forms.BooleanField,
|
||||
'serializer_class': serializers.BooleanField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Show button to copy user input from other products"),
|
||||
),
|
||||
}
|
||||
}
|
||||
PERSON_NAME_TITLE_GROUPS = OrderedDict([
|
||||
@@ -1836,7 +1845,7 @@ PERSON_NAME_TITLE_GROUPS = OrderedDict([
|
||||
'Mx',
|
||||
'Dr',
|
||||
'Professor',
|
||||
'Sir'
|
||||
'Sir',
|
||||
))),
|
||||
('german_common', (_('Most common German titles'), (
|
||||
'Dr.',
|
||||
@@ -1844,9 +1853,16 @@ PERSON_NAME_TITLE_GROUPS = OrderedDict([
|
||||
'Prof. Dr.',
|
||||
)))
|
||||
])
|
||||
|
||||
PERSON_NAME_SALUTATIONS = [
|
||||
pgettext_lazy("person_name_salutation", "Ms"),
|
||||
pgettext_lazy("person_name_salutation", "Mr"),
|
||||
]
|
||||
|
||||
PERSON_NAME_SCHEMES = OrderedDict([
|
||||
('given_family', {
|
||||
'fields': (
|
||||
# field_name, label, weight for widget width
|
||||
('given_name', _('Given name'), 1),
|
||||
('family_name', _('Family name'), 1),
|
||||
),
|
||||
@@ -2001,6 +2017,24 @@ PERSON_NAME_SCHEMES = OrderedDict([
|
||||
'_scheme': 'full_transcription',
|
||||
},
|
||||
}),
|
||||
('salutation_title_given_family', {
|
||||
'fields': (
|
||||
('salutation', pgettext_lazy('person_name', 'Salutation'), 1),
|
||||
('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(key, '') for key in ["title", "given_name", "family_name"]) if p
|
||||
),
|
||||
'sample': {
|
||||
'salutation': pgettext_lazy('person_name_sample', 'Mr'),
|
||||
'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_salutation_given_family',
|
||||
},
|
||||
}),
|
||||
])
|
||||
COUNTRIES_WITH_STATE_IN_ADDRESS = {
|
||||
# Source: http://www.bitboost.com/ref/international-address-formats.html
|
||||
@@ -2016,7 +2050,6 @@ COUNTRIES_WITH_STATE_IN_ADDRESS = {
|
||||
'US': (['State', 'Outlying area', 'District'], 'short'),
|
||||
}
|
||||
|
||||
|
||||
settings_hierarkey = Hierarkey(attribute_name='settings')
|
||||
|
||||
for k, v in DEFAULTS.items():
|
||||
@@ -2086,7 +2119,7 @@ class SettingsSandbox:
|
||||
def __delattr__(self, key: str) -> None:
|
||||
del self._event.settings[self._convert_key(key)]
|
||||
|
||||
def get(self, key: str, default: Any=None, as_type: type=str):
|
||||
def get(self, key: str, default: Any = None, as_type: type = str):
|
||||
return self._event.settings.get(self._convert_key(key), default=default, as_type=as_type)
|
||||
|
||||
def set(self, key: str, value: Any):
|
||||
|
||||
@@ -53,7 +53,15 @@ class BaseQuestionsViewMixin:
|
||||
data=(self.request.POST if self.request.method == 'POST' else None),
|
||||
files=(self.request.FILES if self.request.method == 'POST' else None))
|
||||
form.pos = cartpos or orderpos
|
||||
form.show_copy_answers_to_addon_button = form.pos.addon_to and set(form.pos.addon_to.item.questions.all()) & set(form.pos.item.questions.all())
|
||||
form.show_copy_answers_to_addon_button = form.pos.addon_to and (
|
||||
set(form.pos.addon_to.item.questions.all()) & set(form.pos.item.questions.all()) or
|
||||
(form.pos.addon_to.item.admission and form.pos.item.admission and (
|
||||
self.request.event.settings.attendee_names_asked or
|
||||
self.request.event.settings.attendee_emails_asked or
|
||||
self.request.event.settings.attendee_company_asked or
|
||||
self.request.event.settings.attendee_addresses_asked
|
||||
))
|
||||
)
|
||||
if len(form.fields) > 0:
|
||||
formlist.append(form)
|
||||
return formlist
|
||||
|
||||
@@ -83,6 +83,7 @@ class SimpleCheckinListForm(forms.ModelForm):
|
||||
'all_products',
|
||||
'limit_products',
|
||||
'include_pending',
|
||||
'allow_entry_after_exit',
|
||||
]
|
||||
widgets = {
|
||||
'limit_products': forms.CheckboxSelectMultiple(attrs={
|
||||
|
||||
@@ -522,6 +522,7 @@ class EventSettingsForm(SettingsForm):
|
||||
'banner_text_bottom',
|
||||
'order_email_asked_twice',
|
||||
'last_order_modification_date',
|
||||
'checkout_show_copy_answers_button',
|
||||
]
|
||||
|
||||
def clean(self):
|
||||
|
||||
@@ -287,11 +287,11 @@ class EventOrderFilterForm(OrderFilterForm):
|
||||
for i in self.event.items.prefetch_related('variations').all():
|
||||
variations = list(i.variations.all())
|
||||
if variations:
|
||||
choices.append((str(i.pk), _('{product} – Any variation').format(product=i.name)))
|
||||
choices.append((str(i.pk), _('{product} – Any variation').format(product=str(i))))
|
||||
for v in variations:
|
||||
choices.append(('%d-%d' % (i.pk, v.pk), '%s – %s' % (i.name, v.value)))
|
||||
choices.append(('%d-%d' % (i.pk, v.pk), '%s – %s' % (str(i), v.value)))
|
||||
else:
|
||||
choices.append((str(i.pk), i.name))
|
||||
choices.append((str(i.pk), str(i)))
|
||||
self.fields['item'].choices = choices
|
||||
|
||||
def filter_qs(self, qs):
|
||||
@@ -827,6 +827,8 @@ class CheckInFilterForm(FilterForm):
|
||||
'-item': ('-item__name', '-variation__value', '-order__code'),
|
||||
'seat': ('seat__sorting_rank', 'seat__guid'),
|
||||
'-seat': ('-seat__sorting_rank', '-seat__guid'),
|
||||
'date': ('subevent__date_from', 'order__code'),
|
||||
'-date': ('-subevent__date_from', '-order__code'),
|
||||
'name': {'_order': F('display_name').asc(nulls_first=True),
|
||||
'display_name': Coalesce('attendee_name_cached', 'addon_to__attendee_name_cached')},
|
||||
'-name': {'_order': F('display_name').desc(nulls_last=True),
|
||||
|
||||
@@ -193,7 +193,7 @@ class DeviceForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Device
|
||||
fields = ['name', 'all_events', 'limit_events']
|
||||
fields = ['name', 'all_events', 'limit_events', 'security_profile']
|
||||
widgets = {
|
||||
'limit_events': forms.CheckboxSelectMultiple(attrs={
|
||||
'data-inverse-dependency': '#id_all_events',
|
||||
|
||||
@@ -19,6 +19,12 @@ def render_label(content, label_for=None, label_class=None, label_title='', opti
|
||||
attrs['class'] = label_class
|
||||
if label_title:
|
||||
attrs['title'] = label_title
|
||||
|
||||
if text_value(content) == ' ':
|
||||
# Empty label, e.g. checkbox
|
||||
attrs.setdefault('class', '')
|
||||
attrs['class'] += ' label-empty'
|
||||
|
||||
builder = '<{tag}{attrs}>{content}{opt}</{tag}>'
|
||||
return format_html(
|
||||
builder,
|
||||
|
||||
@@ -2,7 +2,6 @@ from datetime import timedelta
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.forms import formset_factory
|
||||
from django.urls import reverse
|
||||
from django.utils.dates import MONTHS, WEEKDAYS
|
||||
@@ -382,12 +381,6 @@ class TimeForm(forms.Form):
|
||||
required=False
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
d = super().clean()
|
||||
if d.get('time_from') and d.get('time_to') and d['time_from'] > d['time_to']:
|
||||
raise ValidationError({'time_to': _('The end of the event has to be later than its start.')})
|
||||
return d
|
||||
|
||||
|
||||
TimeFormSet = formset_factory(
|
||||
TimeForm,
|
||||
|
||||
@@ -128,7 +128,7 @@ class VoucherForm(I18nModelForm):
|
||||
def clean(self):
|
||||
data = super().clean()
|
||||
|
||||
if not self._errors and self.data.get('itemvar'):
|
||||
if not self._errors:
|
||||
try:
|
||||
itemid = quotaid = None
|
||||
iv = self.data.get('itemvar', '')
|
||||
@@ -136,8 +136,10 @@ class VoucherForm(I18nModelForm):
|
||||
quotaid = iv[2:]
|
||||
elif '-' in iv:
|
||||
itemid, varid = iv.split('-')
|
||||
else:
|
||||
elif iv:
|
||||
itemid, varid = iv, None
|
||||
else:
|
||||
itemid, varid = None, None
|
||||
|
||||
if itemid:
|
||||
self.instance.item = self.instance.event.items.get(pk=itemid)
|
||||
@@ -146,11 +148,15 @@ class VoucherForm(I18nModelForm):
|
||||
else:
|
||||
self.instance.variation = None
|
||||
self.instance.quota = None
|
||||
|
||||
else:
|
||||
elif quotaid:
|
||||
self.instance.quota = self.instance.event.quotas.get(pk=quotaid)
|
||||
self.instance.item = None
|
||||
self.instance.variation = None
|
||||
else:
|
||||
self.instance.quota = None
|
||||
self.instance.item = None
|
||||
self.instance.variation = None
|
||||
|
||||
except ObjectDoesNotExist:
|
||||
raise ValidationError(_("Invalid product selected."))
|
||||
|
||||
|
||||
@@ -26,8 +26,10 @@
|
||||
<li>{{ scope }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p>{% trans "Please select the organizer accounts this application should get access to:" %}</p>
|
||||
{% bootstrap_field form.organizers layout="inline" %}
|
||||
{% if form.organizers %}
|
||||
<p>{% trans "Please select the organizer accounts this application should get access to:" %}</p>
|
||||
{% bootstrap_field form.organizers layout="inline" %}
|
||||
{% endif %}
|
||||
|
||||
{% bootstrap_form_errors form layout="control" %}
|
||||
<p class="text-danger">
|
||||
|
||||
@@ -10,6 +10,23 @@
|
||||
<dd>Stripe Terminal</dd>
|
||||
<dt>{% trans "ID" %}</dt>
|
||||
<dd>{{ payment_info.payment_data.payment_intent }}</dd>
|
||||
{% elif payment_info.payment_type == "terminal_zvt" %}
|
||||
<dt>{% trans "Payment provider" %}</dt>
|
||||
<dd>{% trans "ZVT Terminal" %}</dd>
|
||||
<dt>{% trans "Trace number" context "terminal_zvt" %}</dt>
|
||||
<dd>{{ payment_info.payment_data.traceNumber }}</dd>
|
||||
<dt>{% trans "Payment type" context "terminal_zvt" %}</dt>
|
||||
<dd>{{ payment_info.payment_data.paymentType }}</dd>
|
||||
<dt>{% trans "Additional text" context "terminal_zvt" %}</dt>
|
||||
<dd>{{ payment_info.payment_data.additionalText }}</dd>
|
||||
<dt>{% trans "Turnover number" context "terminal_zvt" %}</dt>
|
||||
<dd>{{ payment_info.payment_data.turnoverNumber }}</dd>
|
||||
<dt>{% trans "Receipt number" context "terminal_zvt" %}</dt>
|
||||
<dd>{{ payment_info.payment_data.receiptNumber }}</dd>
|
||||
<dt>{% trans "Card type" context "terminal_zvt" %}</dt>
|
||||
<dd>{{ payment_info.payment_data.cardName }}</dd>
|
||||
<dt>{% trans "Card expiration" context "terminal_zvt" %}</dt>
|
||||
<dd>{{ payment_info.payment_data.expiry }}</dd>
|
||||
{% elif payment_info.payment_type == "sumup" %}
|
||||
<dt>{% trans "Payment provider" %}</dt>
|
||||
<dd>SumUp</dd>
|
||||
|
||||
@@ -66,6 +66,10 @@
|
||||
<a href="?{% url_replace request 'ordering' 'code'%}"><i class="fa fa-caret-up"></i></a></th>
|
||||
<th>{% trans "Item" %} <a href="?{% url_replace request 'ordering' '-item'%}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'item'%}"><i class="fa fa-caret-up"></i></a></th>
|
||||
{% if request.event.has_subevents and not checkinlist.subevent %}
|
||||
<th>{% trans "Date" context "subevents" %} <a href="?{% url_replace request 'ordering' '-date'%}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'date'%}"><i class="fa fa-caret-up"></i></a></th>
|
||||
{% endif %}
|
||||
{% if seats %}
|
||||
<th>{% trans "Seat" %} <a href="?{% url_replace request 'ordering' '-seat'%}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'seat'%}"><i class="fa fa-caret-up"></i></a></th>
|
||||
@@ -99,6 +103,11 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ e.item }}{% if e.variation %} – {{ e.variation }}{% endif %}</td>
|
||||
{% if request.event.has_subevents and not checkinlist.subevent %}
|
||||
<td>
|
||||
{{ e.subevent.name }} – {{ e.subevent.get_date_range_display }} {{ e.subevent.date_from|date:"TIME_FORMAT" }}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if seats %}
|
||||
<td>{{ e.seat|default_if_none:"" }}</td>
|
||||
{% endif %}
|
||||
|
||||
@@ -98,7 +98,9 @@
|
||||
</td>
|
||||
{% if request.event.has_subevents %}
|
||||
{% if cl.subevent %}
|
||||
<td>{{ cl.subevent.name }} – {{ cl.subevent.get_date_range_display }}</td>
|
||||
<td>
|
||||
{{ cl.subevent.name }} – {{ cl.subevent.get_date_range_display }} {{ cl.subevent.date_from|date:"TIME_FORMAT" }}
|
||||
</td>
|
||||
{% else %}
|
||||
<td>
|
||||
<em>{% trans "All" %}</em>
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
{% bootstrap_field sform.attendee_company_required layout="control" %}
|
||||
{% bootstrap_field sform.attendee_addresses_asked layout="control" %}
|
||||
{% bootstrap_field sform.attendee_addresses_required layout="control" %}
|
||||
{% bootstrap_field sform.checkout_show_copy_answers_button layout="control" %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Texts" %}</legend>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% extends "pretixcontrol/items/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load urlreplace %}
|
||||
{% block title %}{% trans "Quotas" %}{% endblock %}
|
||||
{% block inside %}
|
||||
<h1>{% trans "Quotas" %}</h1>
|
||||
@@ -41,12 +42,21 @@
|
||||
<table class="table table-hover table-quotas">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Quota name" %}</th>
|
||||
<th>{% trans "Quota name" %}
|
||||
<a href="?{% url_replace request 'ordering' '-name' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'name' %}"><i class="fa fa-caret-up"></i></a></th>
|
||||
</th>
|
||||
<th>{% trans "Products" %}</th>
|
||||
{% if request.event.has_subevents %}
|
||||
<th>{% trans "Date" context "subevent" %}</th>
|
||||
<th>{% trans "Date" context "subevent" %}
|
||||
<a href="?{% url_replace request 'ordering' '-date' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'date' %}"><i class="fa fa-caret-up"></i></a></th>
|
||||
</th>
|
||||
{% endif %}
|
||||
<th>{% trans "Total capacity" %}</th>
|
||||
<th>{% trans "Total capacity" %}
|
||||
<a href="?{% url_replace request 'ordering' '-size' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'size' %}"><i class="fa fa-caret-up"></i></a></th>
|
||||
</th>
|
||||
<th>{% trans "Capacity left" %}</th>
|
||||
<th class="action-col-2"></th>
|
||||
</tr>
|
||||
@@ -71,7 +81,9 @@
|
||||
</ul>
|
||||
</td>
|
||||
{% if request.event.has_subevents %}
|
||||
<td>{{ q.subevent.name }} – {{ q.subevent.get_date_range_display }}</td>
|
||||
<td>
|
||||
{{ q.subevent.name }} – {{ q.subevent.get_date_range_display }} {{ q.subevent.date_from|date:"TIME_FORMAT" }}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>{% if q.size == None %}Unlimited{% else %}{{ q.size }}{% endif %}</td>
|
||||
<td>{% include "pretixcontrol/items/fragment_quota_availability.html" with availability=q.cached_avail closed=q.closed %}</td>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<td>
|
||||
{{ r.payment_provider.verbose_name }}
|
||||
</td>
|
||||
<td>{{ o.created|date:"SHORT_DATETIME_FORMAT" }}</td>
|
||||
<td>{{ r.created|date:"SHORT_DATETIME_FORMAT" }}</td>
|
||||
<td>{{ r.get_source_display }}</td>
|
||||
<td>
|
||||
<span class="label label-{% if r.state == "external" or r.state == "transit" or r.state == "created" %}warning{% elif r.state == "done" %}success{% else %}danger{% endif %}">
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
{% bootstrap_field form.name layout="control" %}
|
||||
{% bootstrap_field form.all_events layout="control" %}
|
||||
{% bootstrap_field form.limit_events layout="control" %}
|
||||
{% bootstrap_field form.security_profile layout="control" %}
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
|
||||
@@ -508,6 +508,7 @@
|
||||
{% bootstrap_field form.include_pending layout="control" %}
|
||||
{% bootstrap_field form.all_products layout="control" %}
|
||||
{% bootstrap_field form.limit_products layout="control" %}
|
||||
{% bootstrap_field form.allow_entry_after_exit layout="control" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
@@ -536,6 +537,7 @@
|
||||
{% bootstrap_field cl_formset.empty_form.include_pending layout="control" %}
|
||||
{% bootstrap_field cl_formset.empty_form.all_products layout="control" %}
|
||||
{% bootstrap_field cl_formset.empty_form.limit_products layout="control" %}
|
||||
{% bootstrap_field cl_formset.empty_form.allow_entry_after_exit layout="control" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endescapescript %}
|
||||
|
||||
@@ -192,6 +192,7 @@
|
||||
{% bootstrap_field form.include_pending layout="control" %}
|
||||
{% bootstrap_field form.all_products layout="control" %}
|
||||
{% bootstrap_field form.limit_products layout="control" %}
|
||||
{% bootstrap_field form.allow_entry_after_exit layout="control" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
@@ -220,6 +221,7 @@
|
||||
{% bootstrap_field cl_formset.empty_form.include_pending layout="control" %}
|
||||
{% bootstrap_field cl_formset.empty_form.all_products layout="control" %}
|
||||
{% bootstrap_field cl_formset.empty_form.limit_products layout="control" %}
|
||||
{% bootstrap_field cl_formset.empty_form.allow_entry_after_exit layout="control" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endescapescript %}
|
||||
|
||||
@@ -698,6 +698,19 @@ class QuotaList(PaginationMixin, ListView):
|
||||
if self.request.GET.get("subevent", "") != "":
|
||||
s = self.request.GET.get("subevent", "")
|
||||
qs = qs.filter(subevent_id=s)
|
||||
|
||||
valid_orders = {
|
||||
'-date': ('-subevent__date_from', 'name'),
|
||||
'date': ('subevent__date_from', '-name'),
|
||||
'size': ('size', 'name'),
|
||||
'-size': ('-size', '-name'),
|
||||
'name': ('name',),
|
||||
'-name': ('-name',),
|
||||
}
|
||||
|
||||
if self.request.GET.get("ordering", "-date") in valid_orders:
|
||||
qs = qs.order_by(*valid_orders[self.request.GET.get("ordering", "-date")])
|
||||
|
||||
return qs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
@@ -8,7 +8,9 @@ from django.contrib import messages
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.core.files import File
|
||||
from django.db import transaction
|
||||
from django.db.models import Count, Max, Min, Prefetch, ProtectedError, Sum
|
||||
from django.db.models import (
|
||||
Count, Max, Min, OuterRef, Prefetch, ProtectedError, Subquery, Sum,
|
||||
)
|
||||
from django.db.models.functions import Coalesce, Greatest
|
||||
from django.forms import DecimalField, inlineformset_factory
|
||||
from django.http import JsonResponse
|
||||
@@ -30,7 +32,9 @@ from pretix.base.models import (
|
||||
TeamInvite, User,
|
||||
)
|
||||
from pretix.base.models.event import Event, EventMetaProperty, EventMetaValue
|
||||
from pretix.base.models.giftcards import gen_giftcard_secret
|
||||
from pretix.base.models.giftcards import (
|
||||
GiftCardTransaction, gen_giftcard_secret,
|
||||
)
|
||||
from pretix.base.models.organizer import TeamAPIToken
|
||||
from pretix.base.payment import PaymentException
|
||||
from pretix.base.services.export import multiexport
|
||||
@@ -967,8 +971,11 @@ class GiftCardListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
context_object_name = 'giftcards'
|
||||
|
||||
def get_queryset(self):
|
||||
s = GiftCardTransaction.objects.filter(
|
||||
card=OuterRef('pk')
|
||||
).order_by().values('card').annotate(s=Sum('value')).values('s')
|
||||
qs = self.request.organizer.issued_gift_cards.annotate(
|
||||
cached_value=Coalesce(Sum('transactions__value'), Decimal('0.00'))
|
||||
cached_value=Coalesce(Subquery(s), Decimal('0.00'))
|
||||
).order_by('-issuance')
|
||||
if self.filter_form.is_valid():
|
||||
qs = self.filter_form.filter_qs(qs)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import copy
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from dateutil.rrule import DAILY, MONTHLY, WEEKLY, YEARLY, rrule, rruleset
|
||||
from django.contrib import messages
|
||||
@@ -739,11 +739,15 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Crea
|
||||
se = copy.copy(form.instance)
|
||||
|
||||
se.date_from = make_aware(datetime.combine(rdate, t['time_from']), tz)
|
||||
se.date_to = (
|
||||
make_aware(datetime.combine(rdate, t['time_to']), tz)
|
||||
if t.get('time_to')
|
||||
else None
|
||||
)
|
||||
|
||||
if t.get('time_to'):
|
||||
se.date_to = (
|
||||
make_aware(datetime.combine(rdate, t['time_to']), tz)
|
||||
if t.get('time_to') > t.get('time_from')
|
||||
else make_aware(datetime.combine(rdate + timedelta(days=1), t['time_to']), tz)
|
||||
)
|
||||
else:
|
||||
se.date_to = None
|
||||
se.date_admission = (
|
||||
make_aware(datetime.combine(rdate, t['time_admission']), tz)
|
||||
if t.get('time_admission')
|
||||
|
||||
@@ -114,6 +114,9 @@ class ReauthView(TemplateView):
|
||||
u = backend.request_authenticate(request)
|
||||
if u and u == request.user:
|
||||
next_url = backend.get_next_url(request)
|
||||
t = int(time.time())
|
||||
request.session['pretix_auth_login_time'] = t
|
||||
request.session['pretix_auth_last_used'] = t
|
||||
if next_url and url_has_allowed_host_and_scheme(next_url, allowed_hosts=None):
|
||||
return redirect(next_url)
|
||||
return redirect(reverse('control:index'))
|
||||
|
||||
9
src/pretix/helpers/compat.py
Normal file
9
src/pretix/helpers/compat.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import datetime
|
||||
import sys
|
||||
|
||||
|
||||
def date_fromisocalendar(isoyear, isoweek, isoday):
|
||||
if sys.version_info < (3, 8):
|
||||
return datetime.datetime.strptime(f'{isoyear}-W{isoweek}-{isoday}', "%G-W%V-%u")
|
||||
else:
|
||||
return datetime.datetime.fromisocalendar(isoyear, isoweek, isoday)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: 2020-07-30 19:00+0000\n"
|
||||
"Last-Translator: Abdullah <abdullah.gumaijan@gmail.com>\n"
|
||||
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -48,16 +48,16 @@ msgstr "إجمالي الإيرادات"
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "الاتصال الشريط ..."
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr "مجموع"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "تأكيد الدفع الخاص بك ..."
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "الاتصال البنك الذي تتعامل معه ..."
|
||||
|
||||
@@ -366,30 +366,30 @@ msgstr[5] "سيتم إلغاء الحجز تلقائيا بعد {num} دقيقة
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "الرجاء إدخال كمية التذاكر لأحد أنواع التذاكر."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "من٪ (العملة) ق٪ (سعر) ق"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "من٪ (العملة) ق٪ (سعر) ق"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
@@ -45,16 +45,16 @@ msgstr ""
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr ""
|
||||
|
||||
@@ -335,24 +335,24 @@ msgstr[1] ""
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
@@ -45,16 +45,16 @@ msgstr ""
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr ""
|
||||
|
||||
@@ -337,24 +337,24 @@ msgstr[2] ""
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"PO-Revision-Date: 2020-05-19 09:19+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: 2020-09-15 02:00+0000\n"
|
||||
"Last-Translator: Mie Frydensbjerg <mif@aarhus.dk>\n"
|
||||
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
"da/>\n"
|
||||
@@ -46,16 +46,16 @@ msgstr "Omsætning i alt"
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Kontakter Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Bekræfter din betaling …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Kontakter din bank …"
|
||||
|
||||
@@ -312,11 +312,11 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:755
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
msgstr "Klik for at lukke"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:770
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
msgstr "Du har ændringer, der ikke er gemt!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/orderchange.js:25
|
||||
#, fuzzy
|
||||
@@ -334,7 +334,7 @@ msgstr "Antal"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:131
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
msgstr "Ja"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:132
|
||||
#, fuzzy
|
||||
@@ -366,32 +366,32 @@ msgstr[1] "Varerne i din kurv er reserveret for dig i {num} minutter."
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "fra %(currency)s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "fra %(currency)s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
msgstr "Tidszone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
msgstr "Din lokaltid:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||
msgctxt "widget"
|
||||
@@ -452,7 +452,7 @@ msgstr "tilgængelig: %s"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:28
|
||||
msgctxt "widget"
|
||||
msgid "Only available with a voucher"
|
||||
msgstr "Kun tilgængelig med en voucher"
|
||||
msgstr "Kun tilgængelig med en rabatkode"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:29
|
||||
#, javascript-format
|
||||
@@ -497,7 +497,7 @@ msgstr "Fortsæt booking"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:37
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr "Indløs voucher"
|
||||
msgstr "Indløs rabatkode"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:38
|
||||
msgctxt "widget"
|
||||
@@ -507,7 +507,7 @@ msgstr "Indløs"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:39
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr "Voucherkode"
|
||||
msgstr "Rabatkode"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:40
|
||||
msgctxt "widget"
|
||||
@@ -552,7 +552,7 @@ msgstr "Forrige måned"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:48
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr ""
|
||||
msgstr "Næste uge"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||
#, fuzzy
|
||||
@@ -565,7 +565,7 @@ msgstr "Forrige måned"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr ""
|
||||
msgstr "Åbn sædevalg"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
msgid "Mo"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: 2020-08-25 02:00+0000\n"
|
||||
"Last-Translator: Dennis Lichtenthäler <lichtenthaeler@rami.io>\n"
|
||||
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -47,16 +47,16 @@ msgstr "Gesamtumsatz"
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Kontaktiere Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr "Gesamt"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Zahlung wird bestätigt …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Kontaktiere Ihre Bank …"
|
||||
|
||||
@@ -359,24 +359,24 @@ msgstr[1] ""
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Bitte tragen Sie eine Menge für eines der Produkte ein."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "Der Veranstalter behält %(currency)s %(amount)s ein"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "Sie erhalten %(currency)s %(amount)s zurück"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Bitte geben Sie den Betrag ein, den der Veranstalter einbehalten darf."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr "Zeitzone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr "Deine lokale Zeit:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: 2020-08-25 02:00+0000\n"
|
||||
"Last-Translator: Dennis Lichtenthäler <lichtenthaeler@rami.io>\n"
|
||||
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
|
||||
@@ -47,16 +47,16 @@ msgstr "Gesamtumsatz"
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Kontaktiere Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr "Gesamt"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Zahlung wird bestätigt …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Kontaktiere deine Bank …"
|
||||
|
||||
@@ -358,24 +358,24 @@ msgstr[1] ""
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Bitte trage eine Menge für eines der Produkte ein."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "Der Veranstalter behält %(currency)s %(amount)s ein"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "Du erhältst %(currency)s %(amount)s zurück"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Bitte gib den Betrag ein, den der Veranstalter einbehalten darf."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr "Zeitzone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr "Deine lokale Zeit:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+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,16 +46,16 @@ msgstr ""
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr ""
|
||||
|
||||
@@ -336,24 +336,24 @@ msgstr[1] ""
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: 2019-10-03 19:00+0000\n"
|
||||
"Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n"
|
||||
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -47,16 +47,16 @@ msgstr "Συνολικά κέρδη"
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Επικοινωνία με το Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr "Σύνολο"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
#, fuzzy
|
||||
#| msgid "Contacting Stripe …"
|
||||
msgid "Contacting your bank …"
|
||||
@@ -376,30 +376,30 @@ msgstr[1] "Τα είδη στο καλάθι θα παραμείνουν δεσ
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Εισαγάγετε μια ποσότητα για έναν από τους τύπους εισιτηρίων."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "απο %(currency)s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "απο %(currency)s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: 2020-04-27 20:00+0000\n"
|
||||
"Last-Translator: Gonzalo Gabriel Perez <zalitoar@gmail.com>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -47,16 +47,16 @@ msgstr "Ingresos totales"
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Contactando con Stripe…"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Confirmando el pago…"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Contactando con el banco…"
|
||||
|
||||
@@ -373,30 +373,30 @@ msgstr[1] ""
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Por favor, introduce un valor para cada tipo de entrada."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "a partir de %(currency)s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "a partir de %(currency)s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: French\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"PO-Revision-Date: 2020-02-26 03:00+0000\n"
|
||||
"Last-Translator: David100mark <david.hundertmark@gmx.net>\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: 2020-09-15 17:00+0000\n"
|
||||
"Last-Translator: Martin Gross <martin@pc-coholic.de>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
"fr/>\n"
|
||||
"Language: fr\n"
|
||||
@@ -46,16 +46,16 @@ msgstr "chiffre d'affaires total"
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Contacter Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Confirmation de votre paiment…"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Communication avec votre banque …"
|
||||
|
||||
@@ -164,7 +164,7 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:54
|
||||
msgid "Product"
|
||||
msgstr ""
|
||||
msgstr "Produit"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:58
|
||||
#, fuzzy
|
||||
@@ -369,30 +369,30 @@ msgstr[1] "Les articles de votre panier vous sont réservés pour {num} minutes.
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "SVP entrez une quantité pour un de vos types de billets."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "de %(currency)s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "de %(currency)s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: 2020-01-24 08:00+0000\n"
|
||||
"Last-Translator: Prokaj Miklós <mixolid0@gmail.com>\n"
|
||||
"Language-Team: Hungarian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -47,16 +47,16 @@ msgstr "Teljes bevétel"
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Kapcsolatfelvétel Stripe-pal…"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr "Teljes"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "A fizetés megerősítése…"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Kapcsolatfelvétel a bankjával…"
|
||||
|
||||
@@ -364,30 +364,30 @@ msgstr[1] "A kosár tartalma {num} percig foglalva van számodra."
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Adjon meg egy mennyiséget az egyik jegytípusból."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "%(currency) %(price)-tól"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "%(currency) %(price)-tól"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: 2020-06-12 20:00+0000\n"
|
||||
"Last-Translator: Frank <webappconcept@gmail.com>\n"
|
||||
"Language-Team: Italian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -47,16 +47,16 @@ msgstr "Ricavi totali"
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Sto contattando Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr "Totale"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Stiamo processando il tuo pagamento..."
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Sto contattando la tua banca …"
|
||||
|
||||
@@ -363,24 +363,24 @@ msgstr[1] "Gli elementi nel tuo carrello sono riservati per {num} minuti."
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Inserisci la quantità per una tipologia di biglietto."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "L'organizzatore trattiene %(currency)s %(amount)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "Ricevi indietro %(currency)s %(amount)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Inserisci l'importo che l'organizzatore può trattenere."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: 2019-11-13 06:00+0000\n"
|
||||
"Last-Translator: Zane Smite <z.smite@riga-jurmala.com>\n"
|
||||
"Language-Team: Latvian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -48,16 +48,16 @@ msgstr "Apgrozījums kopā"
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Savienojas ar Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr "Kopā"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Jūsu maksājums tiek apstrādāts …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Tiek veidots savienojums ar jūsu banku …"
|
||||
|
||||
@@ -374,30 +374,30 @@ msgstr[2] "Preces jūsu grozā ir rezervētas uz {num} minūtēm."
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Lūdzu, ievadiet nepieciešamo daudzumu izvēlētajam biļešu veidam."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "no %(currency)s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "from %(currency)s %(price)s"
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "no %(currency)s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+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,16 +46,16 @@ msgstr ""
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr ""
|
||||
|
||||
@@ -336,24 +336,24 @@ msgstr[1] ""
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: 2020-08-05 06:00+0000\n"
|
||||
"Last-Translator: Maarten van den Berg <maartenberg1@gmail.com>\n"
|
||||
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -46,16 +46,16 @@ msgstr "Totaalomzet"
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Verbinding maken met Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr "Totaal"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Betaling bevestigen …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Verbinding maken met uw bank …"
|
||||
|
||||
@@ -351,24 +351,24 @@ msgstr[1] ""
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Voer een hoeveelheid voor een van de producten in."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "De organisator houdt %(currency)s %(amount)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "U krijgt %(currency)s %(amount)s terug"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Voer het bedrag in dat de organisator mag houden."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr "Tijdzone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
msgstr "Uw lokale tijd:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-14 12:00+0000\n"
|
||||
"POT-Creation-Date: 2020-10-13 16:13+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
@@ -45,16 +45,16 @@ msgstr ""
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:57
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:146
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:177
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:153
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr ""
|
||||
|
||||
@@ -335,24 +335,24 @@ msgstr[1] ""
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:387
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:386
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:395
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:394
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:411
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:410
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:425
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:438
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:437
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:430
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "Your local time:"
|
||||
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
Reference in New Issue
Block a user