forked from CGM_Public/pretix_original
Compare commits
47 Commits
v2023.9.0
...
sort-sales
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8957ed929a | ||
|
|
a24a36b1e7 | ||
|
|
13ee2b3c5d | ||
|
|
9b77403796 | ||
|
|
c93c2fb6e8 | ||
|
|
5800babdab | ||
|
|
5e3600ac44 | ||
|
|
3af2342d7b | ||
|
|
3d68c83907 | ||
|
|
6366df2c24 | ||
|
|
05ca336c1b | ||
|
|
f643102696 | ||
|
|
33ef50daea | ||
|
|
8071207bf3 | ||
|
|
767bb27175 | ||
|
|
b7f240faf0 | ||
|
|
e0e2b2d7f7 | ||
|
|
10b515f1d1 | ||
|
|
d81c05c444 | ||
|
|
bc0a205f03 | ||
|
|
0e53ddc83b | ||
|
|
0400b577bb | ||
|
|
ec2085f125 | ||
|
|
6430427e3a | ||
|
|
38a1b6a417 | ||
|
|
764d7a2f1c | ||
|
|
e65564234f | ||
|
|
1ec18eb44a | ||
|
|
4c51c28d7a | ||
|
|
eaa134089e | ||
|
|
a015c9ca2a | ||
|
|
d8ca865fc6 | ||
|
|
987b02d733 | ||
|
|
3dd8d4349d | ||
|
|
fd43908e4c | ||
|
|
257ed8ebc3 | ||
|
|
dfd8bf1c0f | ||
|
|
0f709c2275 | ||
|
|
3b64e6046c | ||
|
|
26cbc24a10 | ||
|
|
33a5479809 | ||
|
|
000c64755d | ||
|
|
b32249d48b | ||
|
|
c325cc1120 | ||
|
|
8d2791b32e | ||
|
|
466fc15382 | ||
|
|
86cf3be225 |
@@ -1,5 +1,7 @@
|
||||
.. spelling:word-list:: checkin
|
||||
|
||||
.. _rest-exporters:
|
||||
|
||||
Data exporters
|
||||
==============
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ at :ref:`plugin-docs`.
|
||||
webhooks
|
||||
seatingplans
|
||||
exporters
|
||||
scheduled_exports
|
||||
shredders
|
||||
sendmail_rules
|
||||
billing_invoices
|
||||
|
||||
@@ -18,6 +18,8 @@ default_price money (string) The price set d
|
||||
price money (string) The price used for this variation. This is either the
|
||||
same as ``default_price`` if that value is set or equal
|
||||
to the item's ``default_price`` (read-only).
|
||||
free_price_suggestion money (string) A suggested price, used as a default value if
|
||||
``Item.free_price`` is set (or ``null``).
|
||||
original_price money (string) An original price, shown for comparison, not used
|
||||
for price calculations (or ``null``).
|
||||
active boolean If ``false``, this variation will not be sold or shown.
|
||||
@@ -53,6 +55,10 @@ meta_data object Values set for
|
||||
|
||||
The ``meta_data`` and ``checkin_attention`` attributes have been added.
|
||||
|
||||
.. versionchanged:: 2023.10
|
||||
|
||||
The ``free_price_suggestion`` attribute has been added.
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
@@ -103,6 +109,7 @@ Endpoints
|
||||
"default_price": "223.00",
|
||||
"price": 223.0,
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"meta_data": {}
|
||||
},
|
||||
{
|
||||
@@ -116,10 +123,16 @@ Endpoints
|
||||
"require_membership": false,
|
||||
"require_membership_hidden": false,
|
||||
"require_membership_types": [],
|
||||
"sales_channels": ["web"],
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": {},
|
||||
"position": 1,
|
||||
"default_price": null,
|
||||
"price": 15.0,
|
||||
"default_price": "223.00",
|
||||
"price": 223.0,
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"meta_data": {}
|
||||
}
|
||||
]
|
||||
@@ -163,6 +176,7 @@ Endpoints
|
||||
"default_price": "10.00",
|
||||
"price": "10.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
@@ -231,6 +245,7 @@ Endpoints
|
||||
"default_price": "10.00",
|
||||
"price": "10.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
@@ -291,6 +306,7 @@ Endpoints
|
||||
"default_price": "10.00",
|
||||
"price": "10.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": false,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
|
||||
@@ -29,6 +29,8 @@ free_price boolean If ``true``,
|
||||
they buy the product (however, the price can't be set
|
||||
lower than the price defined by ``default_price`` or
|
||||
otherwise).
|
||||
free_price_suggestion money (string) A suggested price, used as a default value if
|
||||
``free_price`` is set (or ``null``).
|
||||
tax_rate decimal (string) The VAT rate to be applied for this item (read-only,
|
||||
set through ``tax_rule``).
|
||||
tax_rule integer The internal ID of the applied tax rule (or ``null``).
|
||||
@@ -50,9 +52,12 @@ available_from datetime The first dat
|
||||
(or ``null``).
|
||||
available_until datetime The last date time at which this item can be bought
|
||||
(or ``null``).
|
||||
hidden_if_available integer The internal ID of a quota object, or ``null``. If
|
||||
hidden_if_available integer **DEPRECATED** The internal ID of a quota object, or ``null``. If
|
||||
set, this item won't be shown publicly as long as this
|
||||
quota is available.
|
||||
hidden_if_item_available integer The internal ID of a different item, or ``null``. If
|
||||
set, this item won't be shown publicly as long as this
|
||||
other item is available.
|
||||
require_voucher boolean If ``true``, this item can only be bought using a
|
||||
voucher that is specifically assigned to this item.
|
||||
hide_without_voucher boolean If ``true``, this item is only shown during the voucher
|
||||
@@ -123,6 +128,8 @@ variations list of objects A list with o
|
||||
├ price money (string) The price used for this variation. This is either the
|
||||
same as ``default_price`` if that value is set or equal
|
||||
to the item's ``default_price``.
|
||||
├ free_price_suggestion money (string) A suggested price, used as a default value if
|
||||
``free_price`` is set (or ``null``).
|
||||
├ original_price money (string) An original price, shown for comparison, not used
|
||||
for price calculations (or ``null``).
|
||||
├ active boolean If ``false``, this variation will not be sold or shown.
|
||||
@@ -196,6 +203,15 @@ meta_data object Values set fo
|
||||
|
||||
The ``media_policy`` and ``media_type`` attributes have been added.
|
||||
|
||||
.. versionchanged:: 2023.10
|
||||
|
||||
The ``free_price_suggestion`` and ``variations[x].free_price_suggestion`` attributes have been added.
|
||||
|
||||
.. versionchanged:: 2023.10
|
||||
|
||||
The ``hidden_if_item_available`` attributes has been added, the ``hidden_if_available`` attribute has been
|
||||
deprecated.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
@@ -246,6 +262,7 @@ Endpoints
|
||||
"active": true,
|
||||
"description": null,
|
||||
"free_price": false,
|
||||
"free_price_suggestion": null,
|
||||
"tax_rate": "0.00",
|
||||
"tax_rule": 1,
|
||||
"admission": false,
|
||||
@@ -259,6 +276,7 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -291,6 +309,7 @@ Endpoints
|
||||
"default_price": "10.00",
|
||||
"price": "10.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
@@ -309,6 +328,7 @@ Endpoints
|
||||
"default_price": null,
|
||||
"price": "23.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
@@ -377,6 +397,7 @@ Endpoints
|
||||
"active": true,
|
||||
"description": null,
|
||||
"free_price": false,
|
||||
"free_price_suggestion": null,
|
||||
"tax_rate": "0.00",
|
||||
"tax_rule": 1,
|
||||
"admission": false,
|
||||
@@ -390,6 +411,7 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -422,6 +444,7 @@ Endpoints
|
||||
"default_price": "10.00",
|
||||
"price": "10.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
@@ -440,6 +463,7 @@ Endpoints
|
||||
"default_price": null,
|
||||
"price": "23.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
@@ -489,6 +513,7 @@ Endpoints
|
||||
"active": true,
|
||||
"description": null,
|
||||
"free_price": false,
|
||||
"free_price_suggestion": null,
|
||||
"tax_rate": "0.00",
|
||||
"tax_rule": 1,
|
||||
"admission": false,
|
||||
@@ -502,6 +527,7 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -533,6 +559,7 @@ Endpoints
|
||||
"default_price": "10.00",
|
||||
"price": "10.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
@@ -551,6 +578,7 @@ Endpoints
|
||||
"default_price": null,
|
||||
"price": "23.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
@@ -588,6 +616,7 @@ Endpoints
|
||||
"active": true,
|
||||
"description": null,
|
||||
"free_price": false,
|
||||
"free_price_suggestion": null,
|
||||
"tax_rate": "0.00",
|
||||
"tax_rule": 1,
|
||||
"admission": false,
|
||||
@@ -601,6 +630,7 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -633,6 +663,7 @@ Endpoints
|
||||
"default_price": "10.00",
|
||||
"price": "10.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
@@ -651,6 +682,7 @@ Endpoints
|
||||
"default_price": null,
|
||||
"price": "23.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
@@ -719,6 +751,7 @@ Endpoints
|
||||
"active": true,
|
||||
"description": null,
|
||||
"free_price": false,
|
||||
"free_price_suggestion": null,
|
||||
"tax_rate": "0.00",
|
||||
"tax_rule": 1,
|
||||
"admission": false,
|
||||
@@ -732,6 +765,7 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"generate_tickets": null,
|
||||
@@ -764,6 +798,7 @@ Endpoints
|
||||
"default_price": "10.00",
|
||||
"price": "10.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
@@ -782,6 +817,7 @@ Endpoints
|
||||
"default_price": null,
|
||||
"price": "23.00",
|
||||
"original_price": null,
|
||||
"free_price_suggestion": null,
|
||||
"active": true,
|
||||
"checkin_attention": false,
|
||||
"require_approval": false,
|
||||
|
||||
@@ -348,7 +348,7 @@ Endpoints
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PATCH /api/v1/organizers/bigevents/events/sampleconf/items/1/ HTTP/1.1
|
||||
PATCH /api/v1/organizers/bigevents/events/sampleconf/questions/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
@@ -415,7 +415,7 @@ Endpoints
|
||||
:param event: The ``slug`` field of the event to modify
|
||||
:param id: The ``id`` field of the question to modify
|
||||
:statuscode 200: no error
|
||||
:statuscode 400: The item could not be modified due to invalid submitted data
|
||||
:statuscode 400: The question could not be modified due to invalid submitted data
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
|
||||
|
||||
@@ -427,7 +427,7 @@ Endpoints
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /api/v1/organizers/bigevents/events/sampleconf/items/1/ HTTP/1.1
|
||||
DELETE /api/v1/organizers/bigevents/events/sampleconf/questions/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
@@ -440,7 +440,7 @@ Endpoints
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to modify
|
||||
:param event: The ``slug`` field of the event to modify
|
||||
:param id: The ``id`` field of the item to delete
|
||||
:param id: The ``id`` field of the question to delete
|
||||
:statuscode 204: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.
|
||||
|
||||
556
doc/api/resources/scheduled_exports.rst
Normal file
556
doc/api/resources/scheduled_exports.rst
Normal file
@@ -0,0 +1,556 @@
|
||||
.. spelling:word-list:: checkin
|
||||
|
||||
Scheduled data exports
|
||||
======================
|
||||
|
||||
pretix and it's plugins include a number of data exporters that allow you to bulk download various data from pretix in
|
||||
different formats. You should read :ref:`rest-exporters` first to get an understanding of the basic mechanism.
|
||||
|
||||
Exports can be scheduled to be sent at specific times automatically, both on organizer level and event level.
|
||||
|
||||
Scheduled export resource
|
||||
-------------------------
|
||||
|
||||
The scheduled export contains the following public fields:
|
||||
|
||||
.. rst-class:: rest-resource-table
|
||||
|
||||
===================================== ========================== =======================================================
|
||||
Field Type Description
|
||||
===================================== ========================== =======================================================
|
||||
id integer Internal ID of the schedule
|
||||
owner string Email address of the user who created this schedule (read-only).
|
||||
This address will always receive the export and the export
|
||||
will only contain data that this user has permission
|
||||
to access at the time of the export. **We consider this
|
||||
field experimental, it's behaviour might change in the future.
|
||||
Note that the email address of a user can change at any time.**
|
||||
export_identifier string Identifier of the export to run, see :ref:`rest-exporters`
|
||||
export_form_data object Input data for the export, format depends on the export,
|
||||
see :ref:`rest-exporters` for more details.
|
||||
locale string Language to run the export in
|
||||
mail_additional_recipients string Email addresses to receive the export, comma-separated (or empty string)
|
||||
mail_additional_recipients_cc string Email addresses to receive the export in copy, comma-separated (or empty string)
|
||||
mail_additional_recipients_bcc string Email addresses to receive the exportin blind copy, comma-separated (or empty string)
|
||||
mail_subject string Subject to use for the email (currently no variables supported)
|
||||
mail_template string Text to use for the email (currently no variables supported)
|
||||
schedule_rrule string Recurrence specification to determine the **days** this
|
||||
schedule runs on in ``RRULE`` syntax following `RFC 5545`_
|
||||
with some restrictions. Only one rule is allowed, only
|
||||
one occurrence per day is allowed, and some features
|
||||
are not supported (``BYMONTHDAY``, ``BYYEARDAY``,
|
||||
``BYEASTER``, ``BYWEEKNO``).
|
||||
schedule_rrule_time time Time of day to run this on on the specified days.
|
||||
Will be interpreted as local time of the event for event-level
|
||||
exports. For organizer-level exports, the timezone is given
|
||||
in the field ``timezone``. The export will never run **before**
|
||||
this time but it **may** run **later**.
|
||||
timezone string Time zone to interpret the schedule in (only for organizer-level exports)
|
||||
schedule_next_run datetime Next planned execution (read-only, computed by server)
|
||||
error_counter integer Number of consecutive times this export failed (read-only).
|
||||
After a number of failures (currently 5), the schedule no
|
||||
longer is executed. Changing parameters resets the value.
|
||||
===================================== ========================== =======================================================
|
||||
|
||||
Special notes on permissions
|
||||
----------------------------
|
||||
|
||||
Permission handling for scheduled exports is more complex than for most other objects. The reason for this is that
|
||||
there are two levels of access control involved here: First, you need permission to access or change the configuration
|
||||
of the scheduled exports in the moment you are doing it. Second, you **continuously** need permission to access the
|
||||
**data** that is exported as part of the schedule. For this reason, scheduled exports always need one user account
|
||||
to be their **owner**.
|
||||
|
||||
Therefore, scheduled exports **must** be created by an API client using :ref:`OAuth authentication <rest-oauth>`.
|
||||
It is impossible to create a scheduled export using token authentication. After the export is created, it can also be
|
||||
modified using token authentication.
|
||||
|
||||
A user or token with the "can change settings" permission for a given organizer or event can see and change
|
||||
**all** scheduled exports created for the respective organizer or event, regardless of who created them.
|
||||
A user without this permission can only see **their own** scheduled exports.
|
||||
A token without this permission can not see scheduled exports as all.
|
||||
|
||||
|
||||
|
||||
Endpoints for event exports
|
||||
---------------------------
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/scheduled_exports/
|
||||
|
||||
Returns a list of all scheduled exports the client has access to.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/scheduled_exports/ 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": [
|
||||
{
|
||||
"id": 1,
|
||||
"owner": "john@example.com",
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "mary@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Order list",
|
||||
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
"schedule_next_run": "2023-10-26T02:00:00Z",
|
||||
"error_counter": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``id``, ``export_identifier``, and ``schedule_next_run``.
|
||||
Default: ``id``
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:statuscode 200: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/scheduled_exports/(id)/
|
||||
|
||||
Returns information on one scheduled export, identified by its ID.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/scheduled_exports/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
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"owner": "john@example.com",
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "mary@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Order list",
|
||||
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
"schedule_next_run": "2023-10-26T02:00:00Z",
|
||||
"error_counter": 0
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:param id: The ``id`` field of the scheduled export to fetch
|
||||
:statuscode 200: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||
|
||||
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/scheduled_exports/
|
||||
|
||||
Schedule a new export.
|
||||
|
||||
.. note:: See above for special notes on permissions.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1/organizers/bigevents/events/sampleconf/scheduled_exports/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "mary@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Order list",
|
||||
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"owner": "john@example.com",
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "mary@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Order list",
|
||||
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
"schedule_next_run": "2023-10-26T02:00:00Z",
|
||||
"error_counter": 0
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer of the event to create an item for
|
||||
:param event: The ``slug`` field of the event to create an item for
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: The item could not be created due to invalid submitted data.
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
|
||||
|
||||
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/scheduled_exports/(id)/
|
||||
|
||||
Update a scheduled export. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
|
||||
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
|
||||
want to change.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PATCH /api/v1/organizers/bigevents/events/sampleconf/scheduled_exports/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
Content-Length: 94
|
||||
|
||||
{
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_this"},
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"owner": "john@example.com",
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "mary@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Order list",
|
||||
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
"schedule_next_run": "2023-10-26T02:00:00Z",
|
||||
"error_counter": 0
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to modify
|
||||
:param event: The ``slug`` field of the event to modify
|
||||
:param id: The ``id`` field of the export to modify
|
||||
:statuscode 200: no error
|
||||
:statuscode 400: The export could not be modified due to invalid submitted data
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
|
||||
|
||||
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/scheduled_exports/(id)/
|
||||
|
||||
Delete a scheduled export.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /api/v1/organizers/bigevents/events/sampleconf/scheduled_exports/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 204 No Content
|
||||
Vary: Accept
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to modify
|
||||
:param event: The ``slug`` field of the event to modify
|
||||
:param id: The ``id`` field of the export to delete
|
||||
:statuscode 204: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.
|
||||
|
||||
Endpoints for organizer exports
|
||||
---------------------------
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/scheduled_exports/
|
||||
|
||||
Returns a list of all scheduled exports the client has access to.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/scheduled_exports/ 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": [
|
||||
{
|
||||
"id": 1,
|
||||
"owner": "john@example.com",
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "mary@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Order list",
|
||||
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
"schedule_next_run": "2023-10-26T02:00:00Z",
|
||||
"timezone": "Europe/Berlin",
|
||||
"error_counter": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``id``, ``export_identifier``, and ``schedule_next_run``.
|
||||
Default: ``id``
|
||||
: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)/scheduled_exports/(id)/
|
||||
|
||||
Returns information on one scheduled export, identified by its ID.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/scheduled_exports/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
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"owner": "john@example.com",
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "mary@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Order list",
|
||||
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
"schedule_next_run": "2023-10-26T02:00:00Z",
|
||||
"timezone": "Europe/Berlin",
|
||||
"error_counter": 0
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param id: The ``id`` field of the scheduled export 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)/scheduled_exports/
|
||||
|
||||
Schedule a new export.
|
||||
|
||||
.. note:: See above for special notes on permissions.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1/organizers/bigevents/scheduled_exports/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "mary@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Order list",
|
||||
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
"timezone": "Europe/Berlin"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"owner": "john@example.com",
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "mary@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Order list",
|
||||
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
"schedule_next_run": "2023-10-26T02:00:00Z",
|
||||
"timezone": "Europe/Berlin",
|
||||
"error_counter": 0
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer of the event to create an item for
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: The item 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)/scheduled_exports/(id)/
|
||||
|
||||
Update a scheduled export. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
|
||||
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
|
||||
want to change.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PATCH /api/v1/organizers/bigevents/scheduled_exports/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
Content-Length: 94
|
||||
|
||||
{
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_this"},
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"owner": "john@example.com",
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "week_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "mary@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Order list",
|
||||
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
"schedule_next_run": "2023-10-26T02:00:00Z",
|
||||
"timezone": "Europe/Berlin",
|
||||
"error_counter": 0
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to modify
|
||||
:param id: The ``id`` field of the export to modify
|
||||
:statuscode 200: no error
|
||||
:statuscode 400: The export 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.
|
||||
|
||||
.. http:delete:: /api/v1/organizers/(organizer)/scheduled_exports/(id)/
|
||||
|
||||
Delete a scheduled export.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /api/v1/organizers/bigevents/scheduled_exports/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 204 No Content
|
||||
Vary: Accept
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to modify
|
||||
:param id: The ``id`` field of the export to delete
|
||||
:statuscode 204: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer does not exist **or** you have no permission to delete this resource.
|
||||
|
||||
|
||||
.. _RFC 5545: https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.5.3
|
||||
@@ -24,3 +24,4 @@ If you want to **create** a plugin, please go to the
|
||||
imported_secrets
|
||||
webinar
|
||||
presale-saml
|
||||
kulturpass
|
||||
|
||||
193
doc/plugins/kulturpass.rst
Normal file
193
doc/plugins/kulturpass.rst
Normal file
@@ -0,0 +1,193 @@
|
||||
KulturPass
|
||||
=========
|
||||
|
||||
.. note::
|
||||
|
||||
Since the KulturPass is specific to event organizers within Germany, the following page is also only provided in
|
||||
German. Should you require assistance with the KulturPass and do not speak this language, please feel free reach
|
||||
out to support@pretix.eu.
|
||||
|
||||
|
||||
Einführung
|
||||
----------
|
||||
Der `KulturPass`_ ist ein Angebot der Bundesregierung für alle, die im laufenden Jahr ihren 18. Geburtstag feiern.
|
||||
Sie erhalten ab ihrem 18. Geburtstag ein Budget von 200 Euro, das sie für Eintrittskarten, Bücher, CDs, Platten und
|
||||
vieles andere einsetzen können. So wird Kultur vor Ort noch einfacher erlebbar. Gleichzeitig stärkt das die Nachfrage
|
||||
bei den Anbietenden.
|
||||
|
||||
Da pretix ein Ticketing-System ist, stellen wir ausschließlich einen automatisierten Prozess für den Verkauf von
|
||||
Eintrittskarten über den KulturPass-Marktplatz bereit.
|
||||
|
||||
|
||||
Registrierung und Einrichtung
|
||||
-----------------------------
|
||||
Um als Unternehmen oder Kultureinrichtung Angebote auf dem KulturPass-Marktplatz anbieten zu können, ist zunächst eine
|
||||
Registerung und die Einrichtung eines "Shops" sowie der dazugehörigen Angebote notwendig.
|
||||
|
||||
1. Registrierung
|
||||
Registrieren Sie sich zunächst unter https://www.kulturpass.de/anbietende/layer als Anbieter. Im Zuge der
|
||||
Registrierung beantworten Sie einige Fragen zu Ihrem Unternehmen/Ihrer Kultureinrichtung, hinterlegen Ihre
|
||||
E-Mail-Adresse und beantworten Fragen zu Ihren Angebotsformen sowie Finanzierung Ihrer Einrichtung.
|
||||
|
||||
2. Anlegen eines KulturPass Shops
|
||||
Nach Ihrer Registrierung müssen Sie der Weitergabe Ihrer Daten an die technische Platform hinter dem KulturPass,
|
||||
Mirakl, zustimmen. Hier benennen Sie auch Ihren Shop.
|
||||
|
||||
3. Identifizierung mit ELSTER-Zertifikat
|
||||
Als nächsten Schritt müssen Sie Ihr Unternehmen oder Ihre Einrichtung mit Hilfe eines sog. ELSTER-Zertifikates
|
||||
identifizieren. Dieses Zertifikat nutzen Sie auch bereits jetzt schon, wenn Sie auf elektronischem Wege mit der
|
||||
Finanzverwaltung kommunizieren.
|
||||
|
||||
4. Ersteinrichtung in pretix
|
||||
Hinterlegen Sie nun die ID-Nummer Ihres KulturPass Marktplatz-Shops sowie einen API-Key in den
|
||||
`Einstellungen Ihres Veranstalterkontos`_ (Veranstalter-Konto -> Einstellungen -> KulturPass). Diese Daten müssen
|
||||
Sie nur einmalig für alle Ihre Veranstaltungen angeben.
|
||||
|
||||
Im `KulturPass-Backend`_ finden Sie die benötigten Informationen indem Sie auf das Benutzer-Symbol in der oberen,
|
||||
rechten Ecke klicken, "Profil" und dann "API Schlüssel" auswählen bzw. indem Sie auf "Einstellungen" in der
|
||||
Navigation links und dann "Shop" auswählen.
|
||||
|
||||
.. note::
|
||||
|
||||
Zu jedem Zeitpunkt kann nur ein Hintergrundsystem mit dem KulturPass-System verbunden sein. Werden
|
||||
unterschiedliche Systeme oder gar mehrere pretix-Veranstalterkonten mit dem gleichen KulturPass-System verbunden,
|
||||
können keine Bestellungen mehr verarbeitet werden und Angebote nicht automatisiert an den KulturPass-Marktplatz
|
||||
übermittelt werden. Eingehende Bestellungen von Jugendlichen werden in diesem Fall automatisch abgelehnt, da diese
|
||||
nicht eindeutig zugeordnet werden können. Ebenso überschreibt die Bereitstellung der Angebote eines Systems die
|
||||
Angebote eines anderen Systems.
|
||||
|
||||
Wenn Sie mehrere Systeme haben, die den KulturPass-Marktplatz bedienen sollen, wenden Sie sich bitte an den
|
||||
KulturPass-Support, um sich einen weiteren Shop einrichten zu lassen.
|
||||
|
||||
5. Aktivierung der KulturPass-Erweiterungen
|
||||
Alle Veranstaltungen, die Sie über den KulturPass anbieten möchten, benötigen die `KulturPass-Erweiterung`_.
|
||||
Aktivieren Sie diese bitte in jeder relevanten Veranstaltung über Einstellungen -> Erweiterungen -> Tab
|
||||
"Integrationen" -> KulturPass.
|
||||
|
||||
6. Konfiguration der Artikel
|
||||
Nachdem die KulturPass-Erweiterung aktiviert wurde, müssen Sie sich entscheiden, welche Produkte Sie über den
|
||||
KulturPass-Marktplatz anbieten möchten. In der Bearbeitungs-Ansicht des jeweiligen Produktes finden Sie hierzu im
|
||||
Tab "Zusätzliche Einstellungen" eine Checkbox "Das Produkt kann mit dem KulturPass erworben werden".
|
||||
|
||||
.. note::
|
||||
|
||||
Die Eigenschaft, dass ein Produkt durch den KulturPass-Marktplatz erworben werden kann, kann für beliebig viele
|
||||
Produkte aktiviert werden. Auf Grund der Funktionsweise des KulturPasses sollten Sie jedoch gerade bei vielen
|
||||
Artikeln mit unterschiedlich hohen Preisen darauf achten, dass die Preisspanne nicht zu hoch ausfällt.
|
||||
|
||||
Aktivieren Sie die Option für drei Produkte für 1, 10 und 100 Euro, so wird Ihr Angebot im KulturPass-Marktplatz
|
||||
für 100 Euro gelistet werden. Dies bedeutet im Umkehrschluss auch, dass das KulturPass-Guthaben eines Jugendlichen
|
||||
auch mindestens 100 Euro betragen muss, damit er Ihr Angebot in Anspruch nehmen kann - auch wenn die betroffene
|
||||
Person lediglich das 1 Euro-Angebot wahrnehmen möchte. Erst mit dem 100 Euro KulturPass-Einlösecode wählt die
|
||||
kaufende Person in Ihrem pretix-Shop aus, welches Produkt erworben werden soll. Ein Restguthaben wird nach dem Kauf
|
||||
automatisch zurückerstattet und dem KulturPass-Konto wieder gutgeschrieben.
|
||||
|
||||
7. Konfiguration des Marktplatz-Eintrages
|
||||
Je nach dem, ob es sich bei Ihrer Veranstaltung um eine Einzelveranstaltung oder eine Veranstaltungsreihe handelt,
|
||||
müssen Sie die folgende Einstellung einmalig oder pro Veranstaltungstermin vornehmen.
|
||||
|
||||
Einzelveranstaltungen konfigurieren Sie über den Menüpunkt "KulturPass" in den Einstellungen Ihrer Veranstaltung;
|
||||
Veranstaltungsreihen beim Anlegen oder Editieren eines jeden einzelnen Termins am Ende der Seite.
|
||||
|
||||
Um eine Veranstaltung oder einen Veranstaltungstermin im KulturPass-Marktplatz anzubieten, aktivieren Sie zunächst
|
||||
die Option "Diese Veranstaltung via KulturPass anbieten". Geben Sie im folgenden die benötigten Informationen an.
|
||||
|
||||
Bitte beachten Sie, dass Sie bei den Angaben präzise Titel und Beschreibungen verwenden, da der KulturPass-
|
||||
Marktplatz ausschließlich die Informationen aus diesem Bereich verwendet. Etwaige andere Informationen die Sie
|
||||
bspw. in den "Text auf Startseite"-Felder eingeben haben, erreichen das KulturPass-System nicht.
|
||||
|
||||
.. note::
|
||||
|
||||
Gerade bei Veranstaltungsreihen nutzen viele pretix-Veranstalter gerne verkürzte Termin-Namen. Ein Schwimmbad würde
|
||||
beispielsweise Ihre Veranstaltungsreihe "Freibad Musterstadt" und die einzelnen Termine nur "Schwimmen" nennen.
|
||||
|
||||
Während dies im pretix-Shop in einem gemeinsamen Kontext wunderbar funktioniert, würde eine Veranstaltung mit dem
|
||||
Titel "Schwimmen" im KulturPass-Marktplatz Informationen vermissen lassen. Wählen Sie daher für das Eingabefeld
|
||||
"Veranstaltungstitel" in der KulturPass-Konfiguration einen sprechenden Wert.
|
||||
|
||||
8. Übermittlung der Angebote
|
||||
Sobald Sie Ihre ersten Veranstaltungen konfiguriert und live geschaltet haben, übermittelt pretix automatisch in
|
||||
regelmäßigen Abständen alle von Ihnen angebotenen Veranstaltungen an das KulturPass System (Mirakl). Bitte beachten
|
||||
Sie jedoch, dass der Import der Produkte und Angebote einige Zeit in Anspruch nehmen kann. Zum einen müssen
|
||||
Angebote initial händisch von den Betreibern der KulturPass-Platform freigegeben werden, zum anderen muss auch eine
|
||||
Synchronisation zwischen dem Hintergrundsystem und der KulturPass-App erfolgen. Auf die Dauer dieser Prozesse hat
|
||||
pretix keinen Einfluss.
|
||||
|
||||
9. Freischalten des Marktplatz-Shops
|
||||
Nachdem pretix erstmalig Angebote an das KulturPass-System übermittelt hat, müssen Sie Ihren Shop KulturPass-Shop
|
||||
einmalig freischalten. Loggen Sie sich hierzu in das `KulturPass-Backend`_ ein.
|
||||
|
||||
|
||||
Verwalten von KulturPass-Bestellungen
|
||||
-------------------------------------
|
||||
Durch die Nutzung der pretix-Integration mit dem KulturPass-System müssen Sie sich - bis auf die Kennzeichnung von
|
||||
Produkten, die per KulturPass erworben werden dürfen, sowie die Bereitstellung von Veranstaltungs-Informationen für den
|
||||
KulturPass-Marktplatz - um nichts kümmern: pretix übermittelt automatisch Ihre Veranstaltungen, wickelt die Einlösung
|
||||
der Tickets ab und führt die Abrechnung mit dem Hintergrund-System durch.
|
||||
|
||||
Für Ihre Kunden verhält sich der KulturPass wie eine Zahlungsmethode im Bestellprozess und wird dort neben Ihren
|
||||
anderen Zahlungsmethoden mit angeboten.
|
||||
|
||||
Die Gelder für mit dem KulturPass bezahlte Tickets erhalten Sie in Form einer Sammel-Überweisung von der Stiftung
|
||||
Digitale Chancen auf das von Ihnen beim KulturPass Onboarding angegeben Bankkonto.
|
||||
|
||||
In Ihrem `KulturPass-Backend`_ können Sie über den Menüpunkt "Buchhaltung" Ihre bereits erfolgten und kommenden
|
||||
Auszahlungen betrachten.
|
||||
|
||||
.. note::
|
||||
|
||||
Es ist von äußerster Wichtigkeit, dass Sie weder die eingehenden Bestellungen noch die Produkte und Angebote im
|
||||
KulturPass-Backend händisch bearbeiten - auch wenn dies möglich wäre.
|
||||
|
||||
Bei händischen Änderungen riskieren Sie, dass die Datenbasis zwischen pretix und dem KulturPass-System divergiert
|
||||
und es zu fehlerhaften Buchungen kommt. Wann immer möglich, sollten Sie Korrekturbuchungen und Änderungen
|
||||
ausschließlich über pretix vornehmen.
|
||||
|
||||
Sollte eine händische Änderung/Korrektur notwendig werden, wenden Sie sich bitte an den pretix-Support, damit wir
|
||||
die Auswirkungen evaluieren und vorab mit Ihnen besprechen können!
|
||||
|
||||
Erstattungen für Stornos und Absagen können Sie wie gehabt über das pretix-Backend vornehmen. Der jeweilige Betrag wird
|
||||
dem KulturPass-Konto dann automatisch gutgeschrieben.
|
||||
|
||||
Da nach Ausgabe eines KulturPass Einlöse-Codes dieser vom Kunden jederzeit oder vom System bei
|
||||
Nicht-(Komplett)Einlösung binnen 48 Stunden storniert werden kann, kann das im KulturPass-Backend angezeigte,
|
||||
auszuzahlende Guthaben fluktuieren. Da in der Regel Auszahlungen frühestens 48 Stunden nach der Aufgabe einer
|
||||
KulturPass-Bestellungen erfolgen, sollte Ihr Guthaben in der Regel nicht ins Negative gehen.
|
||||
|
||||
Ablauf für Kunden
|
||||
-----------------
|
||||
Ihre Kunden erhalten - nachdem sie sich ein eigenes Konto in der KulturPass-App angelegt und sich mit ihrem
|
||||
elektronischen Personalausweis identifiziert haben - ein Guthaben von 200 Euro, welches für Leistungen aus dem
|
||||
KulturPass-Marktplatz eingelöst werden kann.
|
||||
|
||||
Im Falle von Veranstaltungen, die per pretix verkauft werden, wählt der Kunde ein Angebot aus und erhält im folgenden
|
||||
binnen kurzer Zeit (ca. 10-20 Minuten) einen Code und einen Link, um diesen einzulösen. Der Link bringt den Kunden direkt auf die Seite der
|
||||
betreffenden pretix-Veranstaltung. Hier wird der Kunde darauf hingewiesen, für welche Produkte der Code genutzt werden
|
||||
kann.
|
||||
|
||||
Im Bezahlschritt des Verkaufsprozesses wird dem Kunden vorgeschlagen, seinen KulturPass Einlösecode nun zu nutzen, um
|
||||
die gewünschte Leistung zu erhalten.
|
||||
|
||||
Wurde ein Artikel gewählt, welcher günstiger als der Wert des Einlösecodes war, wird das Restguthaben automatisch auf
|
||||
das KulturPass-Konto erstattet.
|
||||
|
||||
Wurden hingegen mehrere Artikel in den Warenkorb gelegt, so kann die Differenz mit einem anderen, regulären
|
||||
Zahlungsmittel erfolgen.
|
||||
|
||||
Einlösecodes, die vom Kunden nicht binnen 48 Stunden eingelöst werden, werden automatisch storniert und dem
|
||||
KulturPass-Konto wieder gutgeschrieben. Dieser Mechanismus greift auch, wenn eine Veranstaltung mittlerweile
|
||||
ausverkauft ist und daher der Einlösecode nicht mehr Nutzbar ist.
|
||||
|
||||
|
||||
Unterstützung
|
||||
-------------
|
||||
Weitergehende Informationen zum KulturPass finden Sie auch auf der `Webseite des KulturPasses`_, sowie im
|
||||
`KulturPass Serviceportal`_.
|
||||
|
||||
|
||||
.. _KulturPass: https://www.kulturpass.de/
|
||||
.. _Einstellungen Ihres Veranstalterkontos: https://pretix.eu/control/organizer/-/settings/kulturpass
|
||||
.. _KulturPass-Erweiterung: https://pretix.eu/control/event/-/-/settings/plugins#tab-0-2-open
|
||||
.. _KulturPass-Backend: https://kulturpass-de.mirakl.net/
|
||||
.. _Webseite des KulturPasses: https://www.kulturpass.de/
|
||||
.. _KulturPass Serviceportal: https://service.kulturpass.de/help/
|
||||
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2023.9.0"
|
||||
__version__ = "2023.10.0.dev0"
|
||||
|
||||
@@ -92,6 +92,7 @@ ALL_LANGUAGES = [
|
||||
('id', _('Indonesian')),
|
||||
('it', _('Italian')),
|
||||
('lv', _('Latvian')),
|
||||
('nb-no', _('Norwegian Bokmål')),
|
||||
('pl', _('Polish')),
|
||||
('pt-pt', _('Portuguese (Portugal)')),
|
||||
('pt-br', _('Portuguese (Brazil)')),
|
||||
|
||||
@@ -20,11 +20,14 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.http import QueryDict
|
||||
from pytz import common_timezones
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from pretix.base.exporter import OrganizerLevelExportMixin
|
||||
from pretix.base.models import ScheduledEventExport, ScheduledOrganizerExport
|
||||
from pretix.base.timeframes import DateFrameField, SerializerDateFrameField
|
||||
|
||||
|
||||
@@ -197,3 +200,92 @@ class JobRunSerializer(serializers.Serializer):
|
||||
raise ValidationError(self.errors)
|
||||
|
||||
return not bool(self._errors)
|
||||
|
||||
|
||||
class ScheduledExportSerializer(serializers.ModelSerializer):
|
||||
schedule_next_run = serializers.DateTimeField(read_only=True)
|
||||
export_identifier = serializers.ChoiceField(choices=[])
|
||||
locale = serializers.ChoiceField(choices=settings.LANGUAGES, default='en')
|
||||
owner = serializers.SlugRelatedField(slug_field='email', read_only=True)
|
||||
error_counter = serializers.IntegerField(read_only=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['export_identifier'].choices = [(e, e) for e in self.context['exporters']]
|
||||
|
||||
def validate(self, attrs):
|
||||
if attrs.get("export_form_data"):
|
||||
identifier = attrs.get('export_identifier', self.instance.export_identifier if self.instance else None)
|
||||
exporter = self.context['exporters'].get(identifier)
|
||||
if exporter:
|
||||
try:
|
||||
JobRunSerializer(exporter=exporter).to_internal_value(attrs["export_form_data"])
|
||||
except ValidationError as e:
|
||||
raise ValidationError({"export_form_data": e.detail})
|
||||
else:
|
||||
raise ValidationError({"export_identifier": ["Unknown exporter."]})
|
||||
return attrs
|
||||
|
||||
def validate_mail_additional_recipients(self, value):
|
||||
d = value.replace(' ', '')
|
||||
if len(d.split(',')) > 25:
|
||||
raise ValidationError('Please enter less than 25 recipients.')
|
||||
return d
|
||||
|
||||
def validate_mail_additional_recipients_cc(self, value):
|
||||
d = value.replace(' ', '')
|
||||
if len(d.split(',')) > 25:
|
||||
raise ValidationError('Please enter less than 25 recipients.')
|
||||
return d
|
||||
|
||||
def validate_mail_additional_recipients_bcc(self, value):
|
||||
d = value.replace(' ', '')
|
||||
if len(d.split(',')) > 25:
|
||||
raise ValidationError('Please enter less than 25 recipients.')
|
||||
return d
|
||||
|
||||
|
||||
class ScheduledEventExportSerializer(ScheduledExportSerializer):
|
||||
|
||||
class Meta:
|
||||
model = ScheduledEventExport
|
||||
fields = [
|
||||
'id',
|
||||
'owner',
|
||||
'export_identifier',
|
||||
'export_form_data',
|
||||
'locale',
|
||||
'mail_additional_recipients',
|
||||
'mail_additional_recipients_cc',
|
||||
'mail_additional_recipients_bcc',
|
||||
'mail_subject',
|
||||
'mail_template',
|
||||
'schedule_rrule',
|
||||
'schedule_rrule_time',
|
||||
'schedule_next_run',
|
||||
'error_counter',
|
||||
]
|
||||
|
||||
|
||||
class ScheduledOrganizerExportSerializer(ScheduledExportSerializer):
|
||||
timezone = serializers.ChoiceField(default=settings.TIME_ZONE, choices=[(a, a) for a in common_timezones])
|
||||
|
||||
class Meta:
|
||||
model = ScheduledOrganizerExport
|
||||
fields = [
|
||||
'id',
|
||||
'owner',
|
||||
'export_identifier',
|
||||
'export_form_data',
|
||||
'locale',
|
||||
'mail_additional_recipients',
|
||||
'mail_additional_recipients_cc',
|
||||
'mail_additional_recipients_bcc',
|
||||
'mail_subject',
|
||||
'mail_template',
|
||||
'schedule_rrule',
|
||||
'schedule_rrule_time',
|
||||
'schedule_next_run',
|
||||
'timezone',
|
||||
'error_counter',
|
||||
]
|
||||
|
||||
@@ -59,7 +59,7 @@ class InlineItemVariationSerializer(I18nAwareModelSerializer):
|
||||
class Meta:
|
||||
model = ItemVariation
|
||||
fields = ('id', 'value', 'active', 'description',
|
||||
'position', 'default_price', 'price', 'original_price', 'require_approval',
|
||||
'position', 'default_price', 'price', 'original_price', 'free_price_suggestion', 'require_approval',
|
||||
'require_membership', 'require_membership_types', 'require_membership_hidden',
|
||||
'checkin_attention', 'available_from', 'available_until',
|
||||
'sales_channels', 'hide_without_voucher', 'meta_data')
|
||||
@@ -83,7 +83,7 @@ class ItemVariationSerializer(I18nAwareModelSerializer):
|
||||
class Meta:
|
||||
model = ItemVariation
|
||||
fields = ('id', 'value', 'active', 'description',
|
||||
'position', 'default_price', 'price', 'original_price', 'require_approval',
|
||||
'position', 'default_price', 'price', 'original_price', 'free_price_suggestion', 'require_approval',
|
||||
'require_membership', 'require_membership_types', 'require_membership_hidden',
|
||||
'checkin_attention', 'available_from', 'available_until',
|
||||
'sales_channels', 'hide_without_voucher', 'meta_data')
|
||||
@@ -234,12 +234,13 @@ class ItemSerializer(I18nAwareModelSerializer):
|
||||
class Meta:
|
||||
model = Item
|
||||
fields = ('id', 'category', 'name', 'internal_name', 'active', 'sales_channels', 'description',
|
||||
'default_price', 'free_price', 'tax_rate', 'tax_rule', 'admission', 'personalized',
|
||||
'position', 'picture', 'available_from', 'available_until',
|
||||
'default_price', 'free_price', 'free_price_suggestion', 'tax_rate', 'tax_rule', 'admission',
|
||||
'personalized', 'position', 'picture', 'available_from', 'available_until',
|
||||
'require_voucher', 'hide_without_voucher', 'allow_cancel', 'require_bundling',
|
||||
'min_per_order', 'max_per_order', 'checkin_attention', 'has_variations', 'variations',
|
||||
'addons', 'bundles', 'original_price', 'require_approval', 'generate_tickets',
|
||||
'show_quota_left', 'hidden_if_available', 'allow_waitinglist', 'issue_giftcard', 'meta_data',
|
||||
'show_quota_left', 'hidden_if_available', 'hidden_if_item_available', 'allow_waitinglist',
|
||||
'issue_giftcard', 'meta_data',
|
||||
'require_membership', 'require_membership_types', 'require_membership_hidden', 'grant_membership_type',
|
||||
'grant_membership_duration_like_event', 'grant_membership_duration_days',
|
||||
'grant_membership_duration_months', 'validity_mode', 'validity_fixed_from', 'validity_fixed_until',
|
||||
|
||||
@@ -63,6 +63,7 @@ orga_router.register(r'teams', organizer.TeamViewSet)
|
||||
orga_router.register(r'devices', organizer.DeviceViewSet)
|
||||
orga_router.register(r'orders', order.OrganizerOrderViewSet)
|
||||
orga_router.register(r'invoices', order.InvoiceViewSet)
|
||||
orga_router.register(r'scheduled_exports', exporters.ScheduledOrganizerExportViewSet)
|
||||
orga_router.register(r'exporters', exporters.OrganizerExportersViewSet, basename='exporters')
|
||||
|
||||
team_router = routers.DefaultRouter()
|
||||
@@ -88,6 +89,7 @@ event_router.register(r'taxrules', event.TaxRuleViewSet)
|
||||
event_router.register(r'waitinglistentries', waitinglist.WaitingListViewSet)
|
||||
event_router.register(r'checkinlists', checkin.CheckinListViewSet)
|
||||
event_router.register(r'cartpositions', cart.CartPositionViewSet)
|
||||
event_router.register(r'scheduled_exports', exporters.ScheduledEventExportViewSet)
|
||||
event_router.register(r'exporters', exporters.EventExportersViewSet, basename='exporters')
|
||||
event_router.register(r'shredders', shredders.EventShreddersViewSet, basename='shredders')
|
||||
event_router.register(r'item_meta_properties', event.ItemMetaPropertiesViewSet)
|
||||
|
||||
@@ -89,6 +89,8 @@ class UpdateRequestSerializer(serializers.Serializer):
|
||||
|
||||
class RSAEncryptedField(serializers.Field):
|
||||
def to_representation(self, value):
|
||||
if isinstance(value, memoryview):
|
||||
value = value.tobytes()
|
||||
public_key = load_pem_public_key(
|
||||
self.context['device'].rsa_pubkey.encode(), Backend()
|
||||
)
|
||||
|
||||
@@ -29,14 +29,20 @@ from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
from rest_framework import status, viewsets
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from pretix.api.pagination import TotalOrderingFilter
|
||||
from pretix.api.serializers.exporters import (
|
||||
ExporterSerializer, JobRunSerializer,
|
||||
ExporterSerializer, JobRunSerializer, ScheduledEventExportSerializer,
|
||||
ScheduledOrganizerExportSerializer,
|
||||
)
|
||||
from pretix.base.exporter import OrganizerLevelExportMixin
|
||||
from pretix.base.models import CachedFile, Device, Event, TeamAPIToken
|
||||
from pretix.base.models import (
|
||||
CachedFile, Device, Event, ScheduledEventExport, ScheduledOrganizerExport,
|
||||
TeamAPIToken,
|
||||
)
|
||||
from pretix.base.services.export import export, multiexport
|
||||
from pretix.base.signals import (
|
||||
register_data_exporters, register_multievent_data_exporters,
|
||||
@@ -199,3 +205,152 @@ class OrganizerExportersViewSet(ExportersMixin, viewsets.ViewSet):
|
||||
'provider': instance.identifier,
|
||||
'form_data': data
|
||||
})
|
||||
|
||||
|
||||
class ScheduledExportersViewSet(viewsets.ModelViewSet):
|
||||
filter_backends = (TotalOrderingFilter,)
|
||||
ordering = ('id',)
|
||||
ordering_fields = ('id', 'export_identifier', 'schedule_next_run')
|
||||
|
||||
|
||||
class ScheduledEventExportViewSet(ScheduledExportersViewSet):
|
||||
serializer_class = ScheduledEventExportSerializer
|
||||
queryset = ScheduledEventExport.objects.none()
|
||||
permission = 'can_view_orders'
|
||||
|
||||
def get_queryset(self):
|
||||
perm_holder = self.request.auth if isinstance(self.request.auth, (TeamAPIToken, Device)) else self.request.user
|
||||
if not perm_holder.has_event_permission(self.request.organizer, self.request.event, 'can_change_event_settings',
|
||||
request=self.request):
|
||||
if self.request.user.is_authenticated:
|
||||
qs = self.request.event.scheduled_exports.filter(owner=self.request.user)
|
||||
else:
|
||||
raise PermissionDenied('Scheduled exports require either permission to change event settings or '
|
||||
'user-specific API access.')
|
||||
else:
|
||||
qs = self.request.event.scheduled_exports
|
||||
return qs.select_related("owner")
|
||||
|
||||
def perform_create(self, serializer):
|
||||
if not self.request.user.is_authenticated:
|
||||
raise PermissionDenied('Creation of exports requires user-specific API access.')
|
||||
serializer.save(event=self.request.event, owner=self.request.user)
|
||||
serializer.instance.compute_next_run()
|
||||
serializer.instance.save(update_fields=["schedule_next_run"])
|
||||
self.request.event.log_action(
|
||||
'pretix.event.export.schedule.added',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data=self.request.data
|
||||
)
|
||||
|
||||
def get_serializer_context(self):
|
||||
ctx = super().get_serializer_context()
|
||||
ctx['event'] = self.request.event
|
||||
ctx['exporters'] = self.exporters
|
||||
return ctx
|
||||
|
||||
@cached_property
|
||||
def exporters(self):
|
||||
responses = register_data_exporters.send(self.request.event)
|
||||
exporters = [response(self.request.event, self.request.organizer) for r, response in responses if response]
|
||||
return {e.identifier: e for e in exporters}
|
||||
|
||||
def perform_update(self, serializer):
|
||||
serializer.save(event=self.request.event)
|
||||
serializer.instance.compute_next_run()
|
||||
serializer.instance.error_counter = 0
|
||||
serializer.instance.error_last_message = None
|
||||
serializer.instance.save(update_fields=["schedule_next_run", "error_counter", "error_last_message"])
|
||||
self.request.event.log_action(
|
||||
'pretix.event.export.schedule.changed',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data=self.request.data
|
||||
)
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
self.request.event.log_action(
|
||||
'pretix.event.export.schedule.deleted',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
)
|
||||
super().perform_destroy(instance)
|
||||
|
||||
|
||||
class ScheduledOrganizerExportViewSet(ScheduledExportersViewSet):
|
||||
serializer_class = ScheduledOrganizerExportSerializer
|
||||
queryset = ScheduledOrganizerExport.objects.none()
|
||||
permission = None
|
||||
|
||||
def get_queryset(self):
|
||||
perm_holder = self.request.auth if isinstance(self.request.auth, (TeamAPIToken, Device)) else self.request.user
|
||||
if not perm_holder.has_organizer_permission(self.request.organizer, 'can_change_organizer_settings',
|
||||
request=self.request):
|
||||
if self.request.user.is_authenticated:
|
||||
qs = self.request.organizer.scheduled_exports.filter(owner=self.request.user)
|
||||
else:
|
||||
raise PermissionDenied('Scheduled exports require either permission to change organizer settings or '
|
||||
'user-specific API access.')
|
||||
else:
|
||||
qs = self.request.organizer.scheduled_exports
|
||||
return qs.select_related("owner")
|
||||
|
||||
def perform_create(self, serializer):
|
||||
if not self.request.user.is_authenticated:
|
||||
raise PermissionDenied('Creation of exports requires user-specific API access.')
|
||||
serializer.save(organizer=self.request.organizer, owner=self.request.user)
|
||||
serializer.instance.compute_next_run()
|
||||
serializer.instance.save(update_fields=["schedule_next_run"])
|
||||
self.request.organizer.log_action(
|
||||
'pretix.organizer.export.schedule.added',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data=self.request.data
|
||||
)
|
||||
|
||||
def get_serializer_context(self):
|
||||
ctx = super().get_serializer_context()
|
||||
ctx['organizer'] = self.request.organizer
|
||||
ctx['exporters'] = self.exporters
|
||||
return ctx
|
||||
|
||||
@cached_property
|
||||
def events(self):
|
||||
if isinstance(self.request.auth, (TeamAPIToken, Device)):
|
||||
return self.request.auth.get_events_with_permission('can_view_orders')
|
||||
elif self.request.user.is_authenticated:
|
||||
return self.request.user.get_events_with_permission('can_view_orders', self.request).filter(
|
||||
organizer=self.request.organizer
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def exporters(self):
|
||||
responses = register_multievent_data_exporters.send(self.request.organizer)
|
||||
exporters = [
|
||||
response(Event.objects.none() if issubclass(response, OrganizerLevelExportMixin) else self.events,
|
||||
self.request.organizer)
|
||||
for r, response in responses if response
|
||||
]
|
||||
return {e.identifier: e for e in exporters}
|
||||
|
||||
def perform_update(self, serializer):
|
||||
serializer.save(organizer=self.request.organizer)
|
||||
serializer.instance.compute_next_run()
|
||||
serializer.instance.error_counter = 0
|
||||
serializer.instance.error_last_message = None
|
||||
serializer.instance.save(update_fields=["schedule_next_run", "error_counter", "error_last_message"])
|
||||
self.request.organizer.log_action(
|
||||
'pretix.organizer.export.schedule.changed',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data=self.request.data
|
||||
)
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
self.request.organizer.log_action(
|
||||
'pretix.organizer.export.schedule.deleted',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
)
|
||||
super().perform_destroy(instance)
|
||||
|
||||
@@ -103,15 +103,17 @@ def get_all_sales_channels():
|
||||
if _ALL_CHANNELS:
|
||||
return _ALL_CHANNELS
|
||||
|
||||
types = OrderedDict()
|
||||
channels = []
|
||||
for recv, ret in register_sales_channels.send(None):
|
||||
if isinstance(ret, (list, tuple)):
|
||||
for r in ret:
|
||||
types[r.identifier] = r
|
||||
channels += ret
|
||||
else:
|
||||
types[ret.identifier] = ret
|
||||
_ALL_CHANNELS = types
|
||||
return types
|
||||
channels.append(ret)
|
||||
channels.sort(key=lambda c: c.identifier)
|
||||
_ALL_CHANNELS = OrderedDict([(c.identifier, c) for c in channels])
|
||||
if 'web' in _ALL_CHANNELS:
|
||||
_ALL_CHANNELS.move_to_end('web', last=False)
|
||||
return _ALL_CHANNELS
|
||||
|
||||
|
||||
class WebshopSalesChannel(SalesChannel):
|
||||
|
||||
@@ -149,6 +149,7 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
|
||||
}
|
||||
if self.organizer:
|
||||
htmlctx['organizer'] = self.organizer
|
||||
htmlctx['color'] = self.organizer.settings.primary_color
|
||||
|
||||
if self.event:
|
||||
htmlctx['event'] = self.event
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.2.4 on 2023-10-25 12:01
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("pretixbase", "0247_checkinlist"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="item",
|
||||
name="free_price_suggestion",
|
||||
field=models.DecimalField(decimal_places=2, max_digits=13, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="itemvariation",
|
||||
name="free_price_suggestion",
|
||||
field=models.DecimalField(decimal_places=2, max_digits=13, null=True),
|
||||
),
|
||||
]
|
||||
22
src/pretix/base/migrations/0249_hidden_if_item_available.py
Normal file
22
src/pretix/base/migrations/0249_hidden_if_item_available.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.2.4 on 2023-10-30 11:50
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("pretixbase", "0248_item_free_price_suggestion"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="item",
|
||||
name="hidden_if_item_available",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="pretixbase.item",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -857,6 +857,10 @@ class Event(EventMixin, LoggedModel):
|
||||
v.item = i
|
||||
v.save(force_insert=True)
|
||||
|
||||
for i in self.items.filter(hidden_if_item_available__isnull=False):
|
||||
i.hidden_if_item_available = item_map[i.hidden_if_item_available_id]
|
||||
i.save()
|
||||
|
||||
for imv in ItemMetaValue.objects.filter(item__event=other):
|
||||
imv.pk = None
|
||||
imv.property = item_meta_properties_map[imv.property_id]
|
||||
|
||||
@@ -79,7 +79,7 @@ class AbstractScheduledExport(LoggedModel):
|
||||
)
|
||||
|
||||
schedule_rrule = models.TextField(
|
||||
null=True, blank=True, validators=[RRuleValidator()]
|
||||
null=True, blank=True, validators=[RRuleValidator(enforce_simple=True)]
|
||||
)
|
||||
schedule_rrule_time = models.TimeField(
|
||||
verbose_name=_("Requested start time"),
|
||||
|
||||
@@ -431,6 +431,12 @@ class Item(LoggedModel):
|
||||
"additional donations for your event. This is currently not supported for products that are "
|
||||
"bought as an add-on to other products.")
|
||||
)
|
||||
free_price_suggestion = models.DecimalField(
|
||||
verbose_name=_("Suggested price"),
|
||||
help_text=_("This price will be used as the default value of the input field. The user can choose a lower "
|
||||
"value, but not lower than the price this product would have without the free price option."),
|
||||
max_digits=13, decimal_places=2, null=True, blank=True,
|
||||
)
|
||||
tax_rule = models.ForeignKey(
|
||||
'TaxRule',
|
||||
verbose_name=_('Sales tax'),
|
||||
@@ -488,13 +494,24 @@ class Item(LoggedModel):
|
||||
'Quota',
|
||||
null=True, blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("Only show after sellout of"),
|
||||
verbose_name=pgettext_lazy("hidden_if_available_legacy", "Only show after sellout of"),
|
||||
help_text=_("If you select a quota here, this product will only be shown when that quota is "
|
||||
"unavailable. If combined with the option to hide sold-out products, this allows you to "
|
||||
"swap out products for more expensive ones once they are sold out. There might be a short period "
|
||||
"in which both products are visible while all tickets in the referenced quota are reserved, "
|
||||
"but not yet sold.")
|
||||
)
|
||||
hidden_if_item_available = models.ForeignKey(
|
||||
'Item',
|
||||
null=True, blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("Only show after sellout of"),
|
||||
help_text=_("If you select a product here, this product will only be shown when that product is "
|
||||
"sold out. If combined with the option to hide sold-out products, this allows you to "
|
||||
"swap out products for more expensive ones once the cheaper option is sold out. There might "
|
||||
"be a short period in which both products are visible while all tickets of the referenced "
|
||||
"product are reserved, but not yet sold.")
|
||||
)
|
||||
require_voucher = models.BooleanField(
|
||||
verbose_name=_('This product can only be bought using a voucher.'),
|
||||
default=False,
|
||||
@@ -1021,6 +1038,12 @@ class ItemVariation(models.Model):
|
||||
help_text=_('If set, this will be displayed next to the current price to show that the current price is a '
|
||||
'discounted one. This is just a cosmetic setting and will not actually impact pricing.')
|
||||
)
|
||||
free_price_suggestion = models.DecimalField(
|
||||
verbose_name=_("Suggested price"),
|
||||
help_text=_("This price will be used as the default value of the input field. The user can choose a lower "
|
||||
"value, but not lower than the price this product would have without the free price option."),
|
||||
max_digits=13, decimal_places=2, null=True, blank=True,
|
||||
)
|
||||
require_approval = models.BooleanField(
|
||||
verbose_name=_('Require approval'),
|
||||
default=False,
|
||||
|
||||
@@ -817,7 +817,7 @@ class BasePaymentProvider:
|
||||
"""
|
||||
return ""
|
||||
|
||||
def order_change_allowed(self, order: Order) -> bool:
|
||||
def order_change_allowed(self, order: Order, request: HttpRequest=None) -> bool:
|
||||
"""
|
||||
Will be called to check whether it is allowed to change the payment method of
|
||||
an order to this one.
|
||||
@@ -835,7 +835,12 @@ class BasePaymentProvider:
|
||||
return False
|
||||
|
||||
if self.settings.get('_hidden', as_type=bool):
|
||||
return False
|
||||
if request:
|
||||
hashes = set(request.session.get('pretix_unlock_hashes', [])) | set(order.meta_info_data.get('unlock_hashes', []))
|
||||
if hashlib.sha256((self.settings._hidden_seed + self.event.slug).encode()).hexdigest() not in hashes:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
restricted_countries = self.settings.get('_restricted_countries', as_type=list)
|
||||
if restricted_countries:
|
||||
|
||||
@@ -80,7 +80,7 @@ def assign_automatically(event: Event, user_id: int=None, subevent_id: int=None)
|
||||
voucher__isnull=True
|
||||
).select_related('item', 'variation', 'subevent').prefetch_related(
|
||||
'item__quotas', 'variation__quotas'
|
||||
).order_by('-priority', 'created')
|
||||
).order_by('-priority', 'created', 'pk')
|
||||
|
||||
if subevent_id and event.has_subevents:
|
||||
subevent = event.subevents.get(id=subevent_id)
|
||||
|
||||
@@ -1677,6 +1677,8 @@ DEFAULTS = {
|
||||
('gte', _('Only allow changes if the resulting price is higher or equal than the previous price.')),
|
||||
('gt', _('Only allow changes if the resulting price is higher than the previous price.')),
|
||||
('eq', _('Only allow changes if the resulting price is equal to the previous price.')),
|
||||
('gte_paid', _('Allow changes regardless of price, as long as no refund is required (i.e. the resulting '
|
||||
'price is not lower than what has already been paid).')),
|
||||
('any', _('Allow changes regardless of price, even if this results in a refund.')),
|
||||
)
|
||||
),
|
||||
@@ -1686,6 +1688,8 @@ DEFAULTS = {
|
||||
('gte', _('Only allow changes if the resulting price is higher or equal than the previous price.')),
|
||||
('gt', _('Only allow changes if the resulting price is higher than the previous price.')),
|
||||
('eq', _('Only allow changes if the resulting price is equal to the previous price.')),
|
||||
('gte_paid', _('Allow changes regardless of price, as long as no refund is required (i.e. the resulting '
|
||||
'price is not lower than what has already been paid).')),
|
||||
('any', _('Allow changes regardless of price, even if this results in a refund.')),
|
||||
),
|
||||
widget=forms.RadioSelect,
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from dateutil.rrule import rrulestr
|
||||
import calendar
|
||||
|
||||
from dateutil.rrule import DAILY, MONTHLY, WEEKLY, YEARLY, rrule, rrulestr
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import validate_email
|
||||
@@ -40,7 +42,6 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class BanlistValidator:
|
||||
|
||||
banlist = []
|
||||
|
||||
def __call__(self, value):
|
||||
@@ -55,7 +56,6 @@ class BanlistValidator:
|
||||
|
||||
@deconstructible
|
||||
class EventSlugBanlistValidator(BanlistValidator):
|
||||
|
||||
banlist = [
|
||||
'download',
|
||||
'healthcheck',
|
||||
@@ -77,7 +77,6 @@ class EventSlugBanlistValidator(BanlistValidator):
|
||||
|
||||
@deconstructible
|
||||
class OrganizerSlugBanlistValidator(BanlistValidator):
|
||||
|
||||
banlist = [
|
||||
'download',
|
||||
'healthcheck',
|
||||
@@ -98,7 +97,6 @@ class OrganizerSlugBanlistValidator(BanlistValidator):
|
||||
|
||||
@deconstructible
|
||||
class EmailBanlistValidator(BanlistValidator):
|
||||
|
||||
banlist = [
|
||||
settings.PRETIX_EMAIL_NONE_VALUE,
|
||||
]
|
||||
@@ -112,8 +110,45 @@ def multimail_validate(val):
|
||||
|
||||
|
||||
class RRuleValidator:
|
||||
def __init__(self, enforce_simple=False):
|
||||
self.enforce_simple = enforce_simple
|
||||
|
||||
def __call__(self, value):
|
||||
try:
|
||||
rrulestr(value)
|
||||
parsed = rrulestr(value)
|
||||
except Exception:
|
||||
raise ValidationError("Not a valid rrule.")
|
||||
|
||||
if self.enforce_simple:
|
||||
# Validate that only things are used that we can represent in our UI for later editing
|
||||
|
||||
if not isinstance(parsed, rrule):
|
||||
raise ValidationError("Only a single RRULE is allowed, no combination of rules.")
|
||||
|
||||
if parsed._freq not in (YEARLY, MONTHLY, WEEKLY, DAILY):
|
||||
raise ValidationError("Unsupported FREQ value")
|
||||
if parsed._wkst != calendar.firstweekday():
|
||||
raise ValidationError("Unsupported WKST value")
|
||||
if parsed._bysetpos:
|
||||
if len(parsed._bysetpos) > 1:
|
||||
raise ValidationError("Only one BYSETPOS value allowed")
|
||||
if parsed._freq == YEARLY and parsed._bysetpos not in (1, 2, 3, -1):
|
||||
raise ValidationError("BYSETPOS value not allowed, should be 1, 2, 3 or -1")
|
||||
elif parsed._freq == MONTHLY and parsed._bysetpos not in (1, 2, 3, -1):
|
||||
raise ValidationError("BYSETPOS value not allowed, should be 1, 2, 3 or -1")
|
||||
elif parsed._freq not in (YEARLY, MONTHLY):
|
||||
raise ValidationError("BYSETPOS not allowed for this FREQ")
|
||||
if parsed._bymonthday:
|
||||
raise ValidationError("BYMONTHDAY not supported")
|
||||
if parsed._byyearday:
|
||||
raise ValidationError("BYYEARDAY not supported")
|
||||
if parsed._byeaster:
|
||||
raise ValidationError("BYEASTER not supported")
|
||||
if parsed._byweekno:
|
||||
raise ValidationError("BYWEEKNO not supported")
|
||||
if len(parsed._byhour) > 1 or set(parsed._byhour) != {parsed._dtstart.hour}:
|
||||
raise ValidationError("BYHOUR not supported")
|
||||
if len(parsed._byminute) > 1 or set(parsed._byminute) != {parsed._dtstart.minute}:
|
||||
raise ValidationError("BYMINUTE not supported")
|
||||
if len(parsed._bysecond) > 1 or set(parsed._bysecond) != {parsed._dtstart.second}:
|
||||
raise ValidationError("BYSECOND not supported")
|
||||
|
||||
@@ -208,7 +208,7 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
del self.fields['team']
|
||||
else:
|
||||
self.fields['team'].queryset = self.user.teams.filter(organizer=self.organizer)
|
||||
if not self.organizer.settings.get("event_team_provisioning", True, as_type=bool):
|
||||
if self.organizer.pk and not self.organizer.settings.get("event_team_provisioning", True, as_type=bool):
|
||||
self.fields['team'].required = True
|
||||
self.fields['team'].empty_label = None
|
||||
self.fields['team'].initial = 0
|
||||
|
||||
@@ -1599,6 +1599,20 @@ class EventFilterForm(FilterForm):
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
date_from = forms.DateField(
|
||||
label=_('Date from'),
|
||||
required=False,
|
||||
widget=DatePickerWidget({
|
||||
'placeholder': _('Date from'),
|
||||
}),
|
||||
)
|
||||
date_until = forms.DateField(
|
||||
label=_('Date until'),
|
||||
required=False,
|
||||
widget=DatePickerWidget({
|
||||
'placeholder': _('Date until'),
|
||||
}),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.request = kwargs.pop('request')
|
||||
@@ -1680,6 +1694,22 @@ class EventFilterForm(FilterForm):
|
||||
Q(name__icontains=i18ncomp(query)) | Q(slug__icontains=query)
|
||||
)
|
||||
|
||||
if fdata.get('date_until'):
|
||||
date_end = make_aware(datetime.combine(
|
||||
fdata.get('date_until') + timedelta(days=1),
|
||||
time(hour=0, minute=0, second=0, microsecond=0)
|
||||
), get_current_timezone())
|
||||
qs = qs.filter(
|
||||
Q(date_to__isnull=True, date_from__lt=date_end) |
|
||||
Q(date_to__isnull=False, date_to__lt=date_end)
|
||||
)
|
||||
if fdata.get('date_from'):
|
||||
date_start = make_aware(datetime.combine(
|
||||
fdata.get('date_from'),
|
||||
time(hour=0, minute=0, second=0, microsecond=0)
|
||||
), get_current_timezone())
|
||||
qs = qs.filter(date_from__gte=date_start)
|
||||
|
||||
filters_by_property_name = {}
|
||||
for i, p in enumerate(self.meta_properties):
|
||||
d = fdata.get('meta_{}'.format(p.name))
|
||||
|
||||
@@ -45,7 +45,7 @@ from django.db.models import Max
|
||||
from django.forms.formsets import DELETION_FIELD_NAME
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import escape
|
||||
from django.utils.html import escape, format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import (
|
||||
gettext as __, gettext_lazy as _, pgettext_lazy,
|
||||
@@ -387,6 +387,7 @@ class ItemCreateForm(I18nModelForm):
|
||||
'allow_waitinglist',
|
||||
'show_quota_left',
|
||||
'hidden_if_available',
|
||||
'hidden_if_item_available',
|
||||
'require_bundling',
|
||||
'require_membership',
|
||||
'grant_membership_type',
|
||||
@@ -550,19 +551,43 @@ class ItemUpdateForm(I18nModelForm):
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
change_decimal_field(self.fields['default_price'], self.event.currency)
|
||||
self.fields['hidden_if_available'].queryset = self.event.quotas.all()
|
||||
self.fields['hidden_if_available'].widget = Select2(
|
||||
|
||||
if self.instance.hidden_if_available_id:
|
||||
self.fields['hidden_if_available'].queryset = self.event.quotas.all()
|
||||
self.fields['hidden_if_available'].help_text = format_html(
|
||||
"<strong>{}</strong> {}",
|
||||
_("This option is deprecated. For new products, use the newer option below that refers to another "
|
||||
"product instead of a quota."),
|
||||
self.fields['hidden_if_available'].help_text
|
||||
)
|
||||
self.fields['hidden_if_available'].widget = Select2(
|
||||
attrs={
|
||||
'data-model-select2': 'generic',
|
||||
'data-select2-url': reverse('control:event.items.quotas.select2', kwargs={
|
||||
'event': self.event.slug,
|
||||
'organizer': self.event.organizer.slug,
|
||||
}),
|
||||
'data-placeholder': _('Shown independently of other products')
|
||||
}
|
||||
)
|
||||
self.fields['hidden_if_available'].widget.choices = self.fields['hidden_if_available'].choices
|
||||
self.fields['hidden_if_available'].required = False
|
||||
else:
|
||||
del self.fields['hidden_if_available']
|
||||
|
||||
self.fields['hidden_if_item_available'].queryset = self.event.items.exclude(id=self.instance.id)
|
||||
self.fields['hidden_if_item_available'].widget = Select2(
|
||||
attrs={
|
||||
'data-model-select2': 'generic',
|
||||
'data-select2-url': reverse('control:event.items.quotas.select2', kwargs={
|
||||
'data-select2-url': reverse('control:event.items.select2', kwargs={
|
||||
'event': self.event.slug,
|
||||
'organizer': self.event.organizer.slug,
|
||||
}),
|
||||
'data-placeholder': _('Shown independently of other products')
|
||||
}
|
||||
)
|
||||
self.fields['hidden_if_available'].widget.choices = self.fields['hidden_if_available'].choices
|
||||
self.fields['hidden_if_available'].required = False
|
||||
self.fields['hidden_if_item_available'].widget.choices = self.fields['hidden_if_item_available'].choices
|
||||
self.fields['hidden_if_item_available'].required = False
|
||||
|
||||
self.fields['category'].queryset = self.instance.event.categories.all()
|
||||
self.fields['category'].widget = Select2(
|
||||
@@ -577,6 +602,8 @@ class ItemUpdateForm(I18nModelForm):
|
||||
)
|
||||
self.fields['category'].widget.choices = self.fields['category'].choices
|
||||
|
||||
self.fields['free_price_suggestion'].widget.attrs['data-display-dependency'] = '#id_free_price'
|
||||
|
||||
qs = self.event.organizer.membership_types.all()
|
||||
if qs:
|
||||
self.fields['require_membership_types'].queryset = qs
|
||||
@@ -664,6 +691,7 @@ class ItemUpdateForm(I18nModelForm):
|
||||
'picture',
|
||||
'default_price',
|
||||
'free_price',
|
||||
'free_price_suggestion',
|
||||
'tax_rule',
|
||||
'available_from',
|
||||
'available_until',
|
||||
@@ -680,6 +708,7 @@ class ItemUpdateForm(I18nModelForm):
|
||||
'require_bundling',
|
||||
'show_quota_left',
|
||||
'hidden_if_available',
|
||||
'hidden_if_item_available',
|
||||
'issue_giftcard',
|
||||
'require_membership',
|
||||
'require_membership_types',
|
||||
@@ -706,6 +735,7 @@ class ItemUpdateForm(I18nModelForm):
|
||||
'validity_fixed_from': SplitDateTimeField,
|
||||
'validity_fixed_until': SplitDateTimeField,
|
||||
'hidden_if_available': SafeModelChoiceField,
|
||||
'hidden_if_item_available': SafeModelChoiceField,
|
||||
'grant_membership_type': SafeModelChoiceField,
|
||||
'require_membership_types': SafeModelMultipleChoiceField,
|
||||
}
|
||||
@@ -797,6 +827,8 @@ class ItemVariationForm(I18nModelForm):
|
||||
del self.fields['require_membership']
|
||||
del self.fields['require_membership_types']
|
||||
|
||||
self.fields['free_price_suggestion'].widget.attrs['data-display-dependency'] = '#id_free_price'
|
||||
|
||||
self.meta_fields = []
|
||||
meta_defaults = {}
|
||||
if self.instance.pk:
|
||||
@@ -829,6 +861,7 @@ class ItemVariationForm(I18nModelForm):
|
||||
'value',
|
||||
'active',
|
||||
'default_price',
|
||||
'free_price_suggestion',
|
||||
'original_price',
|
||||
'description',
|
||||
'require_approval',
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
{% extends "pretixcontrol/items/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% block title %}{% trans "Delete check-ins" %}{% endblock %}
|
||||
{% block inside %}
|
||||
<h1>{% trans "Delete check-ins" %}</h1>
|
||||
<form action="{% url "control:event.orders.checkinlists.bulk_action" event=request.event.slug organizer=request.event.organizer.slug list=checkinlist.pk %}" method="post" class="form-horizontal" data-asynctask>
|
||||
{% csrf_token %}
|
||||
{% for k, l in request.POST.lists %}
|
||||
{% for v in l %}
|
||||
<input type="hidden" name="{{ k }}" value="{{ v }}">
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
<p>
|
||||
{% blocktrans trimmed count count=cnt %}
|
||||
Are you sure you want to permanently delete the check-ins of <strong>one ticket</strong>.
|
||||
{% plural %}
|
||||
Are you sure you want to permanently delete the check-ins of <strong>{{ count }} tickets</strong>?
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<div class="form-group submit-group">
|
||||
<a href="{% url "control:event.orders.checkinlists" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default btn-cancel">
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
<button type="submit" class="btn btn-danger btn-save">
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -205,22 +205,27 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% if "can_change_orders" in request.eventpermset or "can_checkin_orders" in request.eventpermset %}
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
<span class="fa fa-sign-in" aria-hidden="true"></span>
|
||||
{% trans "Check-In selected attendees" %}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-default btn-save" name="checkout" value="true">
|
||||
<span class="fa fa-sign-out" aria-hidden="true"></span>
|
||||
{% trans "Check-Out selected attendees" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
<button type="submit" class="btn btn-danger btn-save" name="revert" value="true">
|
||||
<span class="fa fa-trash" aria-hidden="true"></span>
|
||||
{% trans "Delete all check-ins of selected attendees" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
<div class="batch-select-actions">
|
||||
{% if "can_change_orders" in request.eventpermset or "can_checkin_orders" in request.eventpermset %}
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
<span class="fa fa-sign-in" aria-hidden="true"></span>
|
||||
{% trans "Check-In selected attendees" %}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-default btn-save" name="checkout" value="true">
|
||||
<span class="fa fa-sign-out" aria-hidden="true"></span>
|
||||
{% trans "Check-Out selected attendees" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
<button type="submit" class="btn btn-danger btn-save" name="revert"
|
||||
formaction="{% url "control:event.orders.checkinlists.bulk_revert" event=request.event.slug organizer=request.event.organizer.slug list=checkinlist.pk %}"
|
||||
data-no-asynctask
|
||||
value="true">
|
||||
<span class="fa fa-trash" aria-hidden="true"></span>
|
||||
{% trans "Delete all check-ins of selected attendees" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
{% endif %}
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
<p>{% blocktrans with name=checkinlist.name %}Are you sure you want to delete the check-in list <strong>{{ name }}</strong>?{% endblocktrans %}</p>
|
||||
{% if checkinlist.checkins.exists > 0 %}
|
||||
<p>{% blocktrans trimmed with num=checkinlist.checkins.count %}
|
||||
{% if checkinlist.checkins.exists %}
|
||||
<div class="alert alert-warning">{% blocktrans trimmed with num=checkinlist.checkins.count %}
|
||||
This will delete the information of <strong>{{ num }}</strong> check-ins as well.
|
||||
{% endblocktrans %}</p>
|
||||
{% endblocktrans %}</div>
|
||||
{% endif %}
|
||||
<div class="form-group submit-group">
|
||||
<a href="{% url "control:event.orders.checkinlists" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
@@ -18,7 +18,11 @@
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
<button type="submit" class="btn btn-danger btn-save">
|
||||
{% trans "Delete" %}
|
||||
{% if checkinlist.checkins.exists %}
|
||||
{% trans "Delete list and all check-ins" %}
|
||||
{% else %}
|
||||
{% trans "Delete" %}
|
||||
{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{% bootstrap_field filter_form.status %}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{% bootstrap_field filter_form.date_from %}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{% bootstrap_field filter_form.date_until %}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{% bootstrap_field filter_form.organizer %}
|
||||
</div>
|
||||
|
||||
@@ -32,6 +32,14 @@
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not request.event.has_subevents and object.hidden_if_item_available and object.hidden_if_item_available.check_quotas.0 == 100 %}
|
||||
<div class="alert alert-warning">
|
||||
{% blocktrans trimmed %}
|
||||
This product is currently not being shown since you configured below that it should only be visible
|
||||
if a certain other product is already sold out.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% block inside %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
{% bootstrap_field form.active layout="control" %}
|
||||
{% bootstrap_field form.value layout="control" %}
|
||||
{% bootstrap_field form.default_price addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field form.free_price_suggestion addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field form.original_price addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field form.description layout="control" %}
|
||||
{% if form.meta_fields %}
|
||||
@@ -170,6 +171,7 @@
|
||||
{% bootstrap_field formset.empty_form.active layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.value layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.default_price addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.free_price_suggestion addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.original_price addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.description layout="control" %}
|
||||
{% if formset.empty_form.meta_fields %}
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
{% bootstrap_field form.default_price addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field form.tax_rule layout="control" %}
|
||||
{% bootstrap_field form.free_price layout="control" %}
|
||||
{% bootstrap_field form.free_price_suggestion addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field form.original_price addon_after=request.event.currency layout="control" %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
@@ -168,7 +169,10 @@
|
||||
{% endif %}
|
||||
{% bootstrap_field form.allow_cancel layout="control" %}
|
||||
{% bootstrap_field form.allow_waitinglist layout="control" %}
|
||||
{% bootstrap_field form.hidden_if_available layout="control" %}
|
||||
{% if form.hidden_if_available %}
|
||||
{% bootstrap_field form.hidden_if_available layout="control" %}
|
||||
{% endif %}
|
||||
{% bootstrap_field form.hidden_if_item_available layout="control" %}
|
||||
</fieldset>
|
||||
{% for v in formsets.values %}
|
||||
<fieldset>
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{% bootstrap_field filter_form.status %}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{% bootstrap_field filter_form.date_from %}
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{% bootstrap_field filter_form.date_until %}
|
||||
</div>
|
||||
{% for mf in meta_fields %}
|
||||
<div class="col-md-3 col-sm-6 col-xs-12">
|
||||
{% bootstrap_field mf %}
|
||||
|
||||
@@ -438,6 +438,7 @@ urlpatterns = [
|
||||
re_path(r'^checkinlists/select2$', typeahead.checkinlist_select2, name='event.orders.checkinlists.select2'),
|
||||
re_path(r'^checkinlists/(?P<list>\d+)/$', checkin.CheckInListShow.as_view(), name='event.orders.checkinlists.show'),
|
||||
re_path(r'^checkinlists/(?P<list>\d+)/simulator$', checkin.CheckInListSimulator.as_view(), name='event.orders.checkinlists.simulator'),
|
||||
re_path(r'^checkinlists/(?P<list>\d+)/bulk_revert$', checkin.CheckInListBulkRevertConfirmView.as_view(), name='event.orders.checkinlists.bulk_revert'),
|
||||
re_path(r'^checkinlists/(?P<list>\d+)/bulk_action$', checkin.CheckInListBulkActionView.as_view(), name='event.orders.checkinlists.bulk_action'),
|
||||
re_path(r'^checkinlists/(?P<list>\d+)/change$', checkin.CheckinListUpdate.as_view(),
|
||||
name='event.orders.checkinlists.edit'),
|
||||
|
||||
@@ -45,7 +45,7 @@ from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import is_aware, make_aware, now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import FormView, ListView
|
||||
from django.views.generic import FormView, ListView, TemplateView
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.api.views.checkin import _redeem_process
|
||||
@@ -191,9 +191,23 @@ class CheckInListShow(EventPermissionRequiredMixin, PaginationMixin, CheckInList
|
||||
return ctx
|
||||
|
||||
|
||||
class CheckInListBulkRevertConfirmView(CheckInListQueryMixin, EventPermissionRequiredMixin, TemplateView):
|
||||
template_name = "pretixcontrol/checkin/bulk_revert_confirm.html"
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.list = get_object_or_404(self.request.event.checkin_lists.all(), pk=kwargs.get("list"))
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return super().get_context_data(
|
||||
**kwargs,
|
||||
cnt=self.get_queryset().count(),
|
||||
checkinlist=self.list,
|
||||
)
|
||||
|
||||
|
||||
class CheckInListBulkActionView(CheckInListQueryMixin, EventPermissionRequiredMixin, AsyncPostView):
|
||||
permission = ('can_change_orders', 'can_checkin_orders')
|
||||
context_object_name = 'device'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.list = get_object_or_404(self.request.event.checkin_lists.all(), pk=kwargs.get("list"))
|
||||
@@ -216,14 +230,15 @@ class CheckInListBulkActionView(CheckInListQueryMixin, EventPermissionRequiredMi
|
||||
if op.order.status == Order.STATUS_PAID or (
|
||||
(self.list.include_pending or op.order.valid_if_pending) and op.order.status == Order.STATUS_PENDING
|
||||
):
|
||||
Checkin.objects.filter(position=op, list=self.list).delete()
|
||||
op.order.log_action('pretix.event.checkin.reverted', data={
|
||||
'position': op.id,
|
||||
'positionid': op.positionid,
|
||||
'list': self.list.pk,
|
||||
'web': True
|
||||
}, user=request.user)
|
||||
op.order.touch()
|
||||
_, deleted = Checkin.objects.filter(position=op, list=self.list).delete()
|
||||
if deleted:
|
||||
op.order.log_action('pretix.event.checkin.reverted', data={
|
||||
'position': op.id,
|
||||
'positionid': op.positionid,
|
||||
'list': self.list.pk,
|
||||
'web': True
|
||||
}, user=request.user)
|
||||
op.order.touch()
|
||||
|
||||
return 'reverted', request.POST.get('returnquery')
|
||||
else:
|
||||
|
||||
@@ -39,6 +39,7 @@ from decimal import Decimal
|
||||
from hashlib import sha1
|
||||
|
||||
import bleach
|
||||
import dateutil
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
@@ -1636,7 +1637,10 @@ class ExportMixin:
|
||||
for k in initial:
|
||||
if initial[k] and k in test_form.fields:
|
||||
try:
|
||||
initial[k] = test_form.fields[k].to_python(initial[k])
|
||||
if isinstance(test_form.fields[k], forms.SplitDateTimeField):
|
||||
initial[k] = dateutil.parser.parse(initial[k])
|
||||
else:
|
||||
initial[k] = test_form.fields[k].to_python(initial[k])
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
|
||||
@@ -5,7 +5,7 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-10-26 12:47+0000\n"
|
||||
"PO-Revision-Date: 2023-10-27 07:35+0000\n"
|
||||
"PO-Revision-Date: 2023-11-03 04:00+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix/de/"
|
||||
">\n"
|
||||
@@ -14,7 +14,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.1\n"
|
||||
"X-Generator: Weblate 5.1.1\n"
|
||||
"X-Poedit-Bookmarks: -1,-1,904,-1,-1,-1,-1,-1,-1,-1\n"
|
||||
|
||||
#: pretix/_base_settings.py:78
|
||||
@@ -4255,7 +4255,7 @@ msgstr "Produktbild"
|
||||
|
||||
#: pretix/base/models/items.py:491
|
||||
msgid "Only show after sellout of"
|
||||
msgstr "Nicht anzeigen, wenn anderes Kontingent verfügbar"
|
||||
msgstr "Nicht anzeigen, wenn anderes Produkt verfügbar"
|
||||
|
||||
#: pretix/base/models/items.py:492
|
||||
msgid ""
|
||||
@@ -16562,7 +16562,7 @@ msgstr "Geräte-ID"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:6
|
||||
msgid "Receipt ID"
|
||||
msgstr "Transaktionsnummer"
|
||||
msgstr "Belegnummer"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:11
|
||||
msgid "ID"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-10-26 12:47+0000\n"
|
||||
"PO-Revision-Date: 2023-10-27 07:35+0000\n"
|
||||
"PO-Revision-Date: 2023-11-03 04:00+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
|
||||
"pretix/pretix/de_Informal/>\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.1\n"
|
||||
"X-Generator: Weblate 5.1.1\n"
|
||||
|
||||
#: pretix/_base_settings.py:78
|
||||
msgid "English"
|
||||
@@ -4252,7 +4252,7 @@ msgstr "Produktbild"
|
||||
|
||||
#: pretix/base/models/items.py:491
|
||||
msgid "Only show after sellout of"
|
||||
msgstr "Nicht anzeigen, wenn anderes Kontingent verfügbar"
|
||||
msgstr "Nicht anzeigen, wenn anderes Produkt verfügbar"
|
||||
|
||||
#: pretix/base/models/items.py:492
|
||||
msgid ""
|
||||
@@ -16535,7 +16535,7 @@ msgstr "Geräte-ID"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:6
|
||||
msgid "Receipt ID"
|
||||
msgstr "Transaktionsnummer"
|
||||
msgstr "Belegnummer"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:11
|
||||
msgid "ID"
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-10-26 12:47+0000\n"
|
||||
"PO-Revision-Date: 2023-09-27 06:59+0000\n"
|
||||
"Last-Translator: Igor Támara <igor@tamarapatino.org>\n"
|
||||
"PO-Revision-Date: 2023-11-07 14:00+0000\n"
|
||||
"Last-Translator: Zona Vip <contacto@zonavip.mx>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"es/>\n"
|
||||
"Language: es\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.0.2\n"
|
||||
"X-Generator: Weblate 5.1.1\n"
|
||||
|
||||
#: pretix/_base_settings.py:78
|
||||
msgid "English"
|
||||
@@ -455,7 +455,7 @@ msgstr "Reembolso del pago completado"
|
||||
|
||||
#: pretix/api/webhooks.py:281
|
||||
msgid "Refund of payment canceled"
|
||||
msgstr "Reembolso del pago anulado."
|
||||
msgstr "Reembolso del pago anulado"
|
||||
|
||||
#: pretix/api/webhooks.py:285
|
||||
msgid "Refund of payment failed"
|
||||
@@ -526,7 +526,7 @@ msgstr "La tienda ha sido desconectada"
|
||||
|
||||
#: pretix/api/webhooks.py:346
|
||||
msgid "Test-Mode of shop has been activated"
|
||||
msgstr "Modo de pruebas de la tienda ha sido activado."
|
||||
msgstr "Modo de pruebas de la tienda ha sido activado"
|
||||
|
||||
#: pretix/api/webhooks.py:350
|
||||
msgid "Test-Mode of shop has been deactivated"
|
||||
@@ -1575,7 +1575,7 @@ msgstr "Activo"
|
||||
#: pretix/control/forms/discounts.py:87 pretix/control/forms/event.py:955
|
||||
#: pretix/control/forms/item.py:545 pretix/control/forms/item.py:781
|
||||
msgid "Sales channels"
|
||||
msgstr "Canales de ventas"
|
||||
msgstr "Canal de ventas"
|
||||
|
||||
#: pretix/base/exporters/items.py:74 pretix/base/models/items.py:420
|
||||
#: pretix/base/models/items.py:1015
|
||||
@@ -1984,7 +1984,7 @@ msgstr "Pagado con {method}"
|
||||
#: pretix/base/exporters/orderlist.py:435
|
||||
#: pretix/base/exporters/orderlist.py:846
|
||||
msgid "Fee type"
|
||||
msgstr "Tipo de tarifa"
|
||||
msgstr "Tarifa por billete"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:437
|
||||
#: pretix/base/exporters/orderlist.py:555
|
||||
@@ -2027,8 +2027,9 @@ msgstr "cancelado"
|
||||
#: pretix/base/exporters/orderlist.py:539
|
||||
#: pretix/base/exporters/orderlist.py:839
|
||||
#: pretix/plugins/checkinlists/exporters.py:733
|
||||
#, fuzzy
|
||||
msgid "Position ID"
|
||||
msgstr "ID de la Posición"
|
||||
msgstr "Posición"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:547
|
||||
#: pretix/base/exporters/orderlist.py:850
|
||||
@@ -2095,7 +2096,7 @@ msgstr "Fecha de inicio"
|
||||
#: pretix/plugins/checkinlists/exporters.py:493
|
||||
#: pretix/plugins/checkinlists/exporters.py:678
|
||||
msgid "End date"
|
||||
msgstr "Fecha de fin"
|
||||
msgstr "Fecha final"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:551
|
||||
#: pretix/base/exporters/orderlist.py:843 pretix/base/models/items.py:674
|
||||
@@ -3010,9 +3011,9 @@ msgid "Invalid placeholder(s): %(value)s"
|
||||
msgstr "Persona(s) interesada(s) inválida(s): %(value)s"
|
||||
|
||||
#: pretix/base/forms/widgets.py:67
|
||||
#, fuzzy, python-format
|
||||
#, python-format
|
||||
msgid "Sample: %s"
|
||||
msgstr "Ciudad de ejemplo"
|
||||
msgstr "Ciudad de ejemplo: %s"
|
||||
|
||||
#: pretix/base/forms/widgets.py:70
|
||||
#, fuzzy, python-brace-format
|
||||
@@ -3476,14 +3477,12 @@ msgid "Server error"
|
||||
msgstr "Error interno del servidor"
|
||||
|
||||
#: pretix/base/models/checkin.py:367
|
||||
#, fuzzy
|
||||
msgid "Ticket blocked"
|
||||
msgstr "Código de ticket"
|
||||
msgstr "Ticket bloqueado"
|
||||
|
||||
#: pretix/base/models/checkin.py:368
|
||||
#, fuzzy
|
||||
msgid "Ticket not valid at this time"
|
||||
msgstr "Este recibo no es válido para esta fecha de evento."
|
||||
msgstr "Ticket no válido en este momento"
|
||||
|
||||
#: pretix/base/models/customers.py:55
|
||||
#, fuzzy
|
||||
@@ -5210,7 +5209,7 @@ msgstr "Localización"
|
||||
#: pretix/base/models/orders.py:228 pretix/control/forms/filter.py:560
|
||||
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/refund_export.html:57
|
||||
msgid "Total amount"
|
||||
msgstr "Monto total"
|
||||
msgstr "Cantidad total"
|
||||
|
||||
#: pretix/base/models/orders.py:232 pretix/base/models/vouchers.py:292
|
||||
msgid ""
|
||||
@@ -5463,8 +5462,9 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/oauth/authorized.html:19
|
||||
#: pretix/control/templates/pretixcontrol/organizers/index.html:6
|
||||
#: pretix/control/templates/pretixcontrol/organizers/index.html:8
|
||||
#, fuzzy
|
||||
msgid "Organizers"
|
||||
msgstr "Organizadores"
|
||||
msgstr "Cuenta del organizador"
|
||||
|
||||
#: pretix/base/models/organizer.py:262
|
||||
#: pretix/control/templates/pretixcontrol/organizers/teams.html:35
|
||||
@@ -6283,10 +6283,11 @@ msgstr "No se encontraron respuestas coincidentes."
|
||||
|
||||
#: pretix/base/orderimport.py:740 pretix/base/services/cart.py:202
|
||||
#: pretix/base/services/orderimport.py:158
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The seat you selected has already been taken. Please select a different seat."
|
||||
msgstr "Esta URL semántica ya está en uso. Por favor, elija una diferente."
|
||||
msgstr ""
|
||||
"El asiento que has seleccionado ya está ocupado. Por favor seleccione un "
|
||||
"asiento diferente."
|
||||
|
||||
#: pretix/base/orderimport.py:743 pretix/base/services/cart.py:199
|
||||
#, fuzzy
|
||||
@@ -7429,9 +7430,8 @@ msgstr ""
|
||||
"complemento de otro proyecto."
|
||||
|
||||
#: pretix/base/services/cart.py:200
|
||||
#, fuzzy
|
||||
msgid "Please select a valid seat."
|
||||
msgstr "Por favor, seleccione una cuota."
|
||||
msgstr "Por favor seleccione un asiento válido."
|
||||
|
||||
#: pretix/base/services/cart.py:201
|
||||
#, fuzzy
|
||||
@@ -10909,7 +10909,7 @@ msgstr "Srx."
|
||||
#: pretix/base/settings.py:3515 pretix/base/settings.py:3536
|
||||
#: pretix/base/settings.py:3558
|
||||
msgid "Given name"
|
||||
msgstr "Nombre dado"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: pretix/base/settings.py:3357 pretix/base/settings.py:3370
|
||||
#: pretix/base/settings.py:3386 pretix/base/settings.py:3402
|
||||
@@ -21649,9 +21649,8 @@ msgstr ""
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:87
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_profile.html:116
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_profile.html:171
|
||||
#, fuzzy
|
||||
msgid "Details"
|
||||
msgstr "Detalles del pedido"
|
||||
msgstr "Detalles"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/customer.html:53
|
||||
#, fuzzy
|
||||
@@ -22775,7 +22774,7 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/pdf/index.html:141
|
||||
#: pretix/control/templates/pretixcontrol/pdf/index.html:173
|
||||
msgid "Loading…"
|
||||
msgstr "Cargando…"
|
||||
msgstr "Cargado…"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/pdf/index.html:144
|
||||
msgid "Start editing"
|
||||
@@ -26958,9 +26957,8 @@ msgid "Search text"
|
||||
msgstr "Buscar"
|
||||
|
||||
#: pretix/plugins/banktransfer/views.py:356
|
||||
#, fuzzy
|
||||
msgid "min"
|
||||
msgstr "Pie de imprenta"
|
||||
msgstr "Información de contacto"
|
||||
|
||||
#: pretix/plugins/banktransfer/views.py:357
|
||||
#, fuzzy
|
||||
@@ -29940,7 +29938,7 @@ msgstr "Contactar con el organizador del evento"
|
||||
#: pretix/presale/templates/pretixpresale/fragment_modals.html:129
|
||||
#: pretix/presale/templates/pretixpresale/organizers/base.html:91
|
||||
msgid "Privacy policy"
|
||||
msgstr ""
|
||||
msgstr "Aviso de Privacidad"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/base.html:176
|
||||
#: pretix/presale/templates/pretixpresale/organizers/base.html:94
|
||||
@@ -29951,7 +29949,7 @@ msgstr "Configuración de la factura"
|
||||
#: pretix/presale/templates/pretixpresale/event/base.html:179
|
||||
#: pretix/presale/templates/pretixpresale/organizers/base.html:97
|
||||
msgid "Imprint"
|
||||
msgstr "Pie de imprenta"
|
||||
msgstr "Información de contacto"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/checkout_addons.html:10
|
||||
msgid ""
|
||||
@@ -30007,9 +30005,8 @@ msgid "Cart expired"
|
||||
msgstr "El carrito de compras ha expirado"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/checkout_base.html:37
|
||||
#, fuzzy
|
||||
msgid "Show full cart"
|
||||
msgstr "Mostrar información"
|
||||
msgstr "Mostrar carrito completo"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/checkout_base.html:49
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:78
|
||||
|
||||
@@ -8,16 +8,16 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-10-18 07:26+0000\n"
|
||||
"PO-Revision-Date: 2021-11-25 21:00+0000\n"
|
||||
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/es/>\n"
|
||||
"PO-Revision-Date: 2023-11-07 14:00+0000\n"
|
||||
"Last-Translator: Zona Vip <contacto@zonavip.mx>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/"
|
||||
"pretix-js/es/>\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.8\n"
|
||||
"X-Generator: Weblate 5.1.1\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -52,7 +52,7 @@ msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:39
|
||||
msgid "Credit Card"
|
||||
msgstr ""
|
||||
msgstr "Tarjeta de crédito"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:40
|
||||
msgid "PayPal Pay Later"
|
||||
@@ -288,16 +288,12 @@ msgid "Ticket code revoked/changed"
|
||||
msgstr "Código de ticket revocado/cambiado"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
|
||||
#, fuzzy
|
||||
#| msgid "Ticket not paid"
|
||||
msgid "Ticket blocked"
|
||||
msgstr "Ticket por pagar"
|
||||
msgstr "Ticket bloqueado"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
|
||||
#, fuzzy
|
||||
#| msgid "Ticket not paid"
|
||||
msgid "Ticket not valid at this time"
|
||||
msgstr "Ticket por pagar"
|
||||
msgstr "Ticket no válido en este momento"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
|
||||
msgid "Order canceled"
|
||||
@@ -427,11 +423,11 @@ msgstr "Ver variaciones del producto"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:101
|
||||
msgid "Gate"
|
||||
msgstr ""
|
||||
msgstr "Puerta"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:105
|
||||
msgid "Current date and time"
|
||||
msgstr "Fecha y hora"
|
||||
msgstr "Fecha y hora actual"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:109
|
||||
msgid "Current day of the week (1 = Monday, 7 = Sunday)"
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-10-26 12:47+0000\n"
|
||||
"PO-Revision-Date: 2022-09-15 21:00+0000\n"
|
||||
"Last-Translator: Svyatoslav <slava@digitalarthouse.eu>\n"
|
||||
"PO-Revision-Date: 2023-11-03 22:00+0000\n"
|
||||
"Last-Translator: Jāzeps Benjamins Baško <jazeps.basko@gmail.com>\n"
|
||||
"Language-Team: Latvian <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"lv/>\n"
|
||||
"Language: lv\n"
|
||||
@@ -18,7 +18,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n % 10 == 0 || n % 100 >= 11 && n % 100 <= "
|
||||
"19) ? 0 : ((n % 10 == 1 && n % 100 != 11) ? 1 : 2);\n"
|
||||
"X-Generator: Weblate 4.14\n"
|
||||
"X-Generator: Weblate 5.1.1\n"
|
||||
|
||||
#: pretix/_base_settings.py:78
|
||||
msgid "English"
|
||||
@@ -42,11 +42,11 @@ msgstr "Ķīniešu (vienkāršota)"
|
||||
|
||||
#: pretix/_base_settings.py:83
|
||||
msgid "Chinese (traditional)"
|
||||
msgstr ""
|
||||
msgstr "Ķīniešu (tradicionālā)"
|
||||
|
||||
#: pretix/_base_settings.py:84
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
msgstr "Čehu"
|
||||
|
||||
#: pretix/_base_settings.py:85
|
||||
msgid "Danish"
|
||||
@@ -78,7 +78,7 @@ msgstr "Grieķu"
|
||||
|
||||
#: pretix/_base_settings.py:92
|
||||
msgid "Indonesian"
|
||||
msgstr ""
|
||||
msgstr "Indonēziešu"
|
||||
|
||||
#: pretix/_base_settings.py:93
|
||||
msgid "Italian"
|
||||
@@ -102,7 +102,7 @@ msgstr "Portugāļu (Brazīlija)"
|
||||
|
||||
#: pretix/_base_settings.py:98
|
||||
msgid "Romanian"
|
||||
msgstr ""
|
||||
msgstr "Rumāņu"
|
||||
|
||||
#: pretix/_base_settings.py:99
|
||||
msgid "Russian"
|
||||
@@ -118,7 +118,7 @@ msgstr "Turku"
|
||||
|
||||
#: pretix/_base_settings.py:102
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
msgstr "Ukraiņu"
|
||||
|
||||
#: pretix/api/auth/devicesecurity.py:31
|
||||
msgid ""
|
||||
@@ -300,10 +300,9 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: pretix/api/serializers/order.py:79
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Your input was not valid."
|
||||
#, python-brace-format
|
||||
msgid "\"{input}\" is not a valid choice."
|
||||
msgstr "Jūsu ievadītais teksts nav derīgs."
|
||||
msgstr "Jūsu ievadītais teksts \"{input}\" nav derīgs."
|
||||
|
||||
#: pretix/api/serializers/order.py:1297 pretix/api/views/cart.py:220
|
||||
#: pretix/base/services/orders.py:1495
|
||||
@@ -668,10 +667,9 @@ msgid "Incompatible SSO provider: \"{error}\"."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/customersso/oidc.py:109
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Your event registration: {code}"
|
||||
#, python-brace-format
|
||||
msgid "You are not requesting \"{scope}\"."
|
||||
msgstr "Jūsu pasākuma reģistrācija: {code}"
|
||||
msgstr "Jūs nepieprasāt \"{scope}\""
|
||||
|
||||
#: pretix/base/customersso/oidc.py:115
|
||||
#, python-brace-format
|
||||
@@ -1032,10 +1030,8 @@ msgstr "Pasākuma biļete {event}-{code}"
|
||||
#: pretix/plugins/reports/exporters.py:434
|
||||
#: pretix/plugins/reports/exporters.py:671
|
||||
#: pretix/plugins/ticketoutputpdf/exporters.py:85
|
||||
#, fuzzy
|
||||
#| msgid "Date and time"
|
||||
msgid "Date range"
|
||||
msgstr "Datums un laiks"
|
||||
msgstr "Datumu apgabals"
|
||||
|
||||
#: pretix/base/exporters/dekodi.py:223 pretix/base/exporters/invoices.py:77
|
||||
#, fuzzy
|
||||
@@ -2687,7 +2683,7 @@ msgstr ""
|
||||
#: pretix/plugins/reports/accountingreport.py:104
|
||||
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:67
|
||||
msgid "All"
|
||||
msgstr "Visi"
|
||||
msgstr "Viss"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1258 pretix/control/forms/filter.py:1317
|
||||
msgid "Live"
|
||||
@@ -3035,10 +3031,9 @@ msgid "Invalid placeholder(s): %(value)s"
|
||||
msgstr "Nederīgs (-i) vietturis (-i): %(value)s"
|
||||
|
||||
#: pretix/base/forms/widgets.py:67
|
||||
#, fuzzy, python-format
|
||||
#| msgid "Sample city"
|
||||
#, python-format
|
||||
msgid "Sample: %s"
|
||||
msgstr "Piemēra pilsēta"
|
||||
msgstr "Piemērs: %s"
|
||||
|
||||
#: pretix/base/forms/widgets.py:70
|
||||
#, python-brace-format
|
||||
@@ -3200,11 +3195,10 @@ msgid "Single price: {net_price} net / {gross_price} gross"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/invoice.py:659
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Single price"
|
||||
#, python-brace-format
|
||||
msgctxt "invoice"
|
||||
msgid "Single price: {price}"
|
||||
msgstr "Vienības cena"
|
||||
msgstr "Vienības cena: {price}"
|
||||
|
||||
#: pretix/base/invoice.py:677 pretix/base/invoice.py:683
|
||||
msgctxt "invoice"
|
||||
@@ -3256,32 +3250,24 @@ msgid "Included taxes"
|
||||
msgstr "Iekļautie nodokļi"
|
||||
|
||||
#: pretix/base/invoice.py:829
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgctxt "invoice"
|
||||
#| msgid ""
|
||||
#| "Using the conversion rate of 1:{rate} as published by the European "
|
||||
#| "Central Bank on {date}, this corresponds to:"
|
||||
#, python-brace-format
|
||||
msgctxt "invoice"
|
||||
msgid ""
|
||||
"Using the conversion rate of 1:{rate} as published by the {authority} on "
|
||||
"{date}, this corresponds to:"
|
||||
msgstr ""
|
||||
"Izmantojot maiņas kursu 1:{rate}, ko Eiropas Centrālā banka publicēja "
|
||||
"{date}, šis atbilst:"
|
||||
"Izmantojot maiņas kursu 1:{rate}, ko {authority} publicēja {date}, šis "
|
||||
"atbilst:"
|
||||
|
||||
#: pretix/base/invoice.py:844
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgctxt "invoice"
|
||||
#| msgid ""
|
||||
#| "Using the conversion rate of 1:{rate} as published by the European "
|
||||
#| "Central Bank on {date}, the invoice total corresponds to {total}."
|
||||
#, python-brace-format
|
||||
msgctxt "invoice"
|
||||
msgid ""
|
||||
"Using the conversion rate of 1:{rate} as published by the {authority} on "
|
||||
"{date}, the invoice total corresponds to {total}."
|
||||
msgstr ""
|
||||
"Izmantojot maiņas kursu 1:{rate}, ko Eiropas Centrālā banka publicēja "
|
||||
"{date}, rēķina kopsumma atbilst {total}."
|
||||
"Izmantojot maiņas kursu 1:{rate}, ko {authority} publicēja {date}, rēķina "
|
||||
"kopsumma atbilst {total}."
|
||||
|
||||
#: pretix/base/invoice.py:858
|
||||
msgid "Modern Invoice Renderer (pretix 2.7)"
|
||||
@@ -4032,10 +4018,8 @@ msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/models/exports.py:59
|
||||
#, fuzzy
|
||||
#| msgid "Additional fee"
|
||||
msgid "Additional recipients"
|
||||
msgstr "Papildu maksa"
|
||||
msgstr "Papildu saņēmēji"
|
||||
|
||||
#: pretix/base/models/exports.py:61 pretix/base/models/exports.py:66
|
||||
#: pretix/base/models/exports.py:71
|
||||
@@ -4043,16 +4027,12 @@ msgid "You can specify multiple recipients separated by commas."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/models/exports.py:64
|
||||
#, fuzzy
|
||||
#| msgid "Additional fee"
|
||||
msgid "Additional recipients (Cc)"
|
||||
msgstr "Papildu maksa"
|
||||
msgstr "Papildu saņēmēji (cc)"
|
||||
|
||||
#: pretix/base/models/exports.py:69
|
||||
#, fuzzy
|
||||
#| msgid "Additional fee"
|
||||
msgid "Additional recipients (Bcc)"
|
||||
msgstr "Papildu maksa"
|
||||
msgstr "Papildu saņēmēji (Bcc)"
|
||||
|
||||
#: pretix/base/models/exports.py:74 pretix/control/forms/event.py:1070
|
||||
#: pretix/control/forms/event.py:1132 pretix/control/forms/event.py:1144
|
||||
@@ -7178,16 +7158,12 @@ msgstr[2] ""
|
||||
#: pretix/base/services/cart.py:134 pretix/base/services/orders.py:154
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:157
|
||||
#: pretix/presale/views/waiting.py:104 pretix/presale/views/widget.py:715
|
||||
#, fuzzy
|
||||
#| msgid "The presale period for this event has not yet started."
|
||||
msgid "The booking period for this event has not yet started."
|
||||
msgstr "Šī pasākuma iepriekšpārdošanas periods vēl nav sācies."
|
||||
msgstr "Šī pasākuma biļešu pārdošanas periods vēl nav sācies."
|
||||
|
||||
#: pretix/base/services/cart.py:135
|
||||
#, fuzzy
|
||||
#| msgid "The presale period for this event has ended."
|
||||
msgid "The booking period for this event has ended."
|
||||
msgstr "Šī pasākuma iepriekšpārdošanas periods ir beidzies."
|
||||
msgstr "Šī pasākuma biļešu pārdošanas periods ir beidzies."
|
||||
|
||||
#: pretix/base/services/cart.py:136
|
||||
msgid ""
|
||||
@@ -7196,28 +7172,20 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/services/cart.py:138
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "The presale period for this event has not yet started. The affected "
|
||||
#| "positions have been removed from your cart."
|
||||
msgid ""
|
||||
"The booking period for this event has not yet started. The affected "
|
||||
"positions have been removed from your cart."
|
||||
msgstr ""
|
||||
"Šī pasākuma iepriekšpārdošanas periods vēl nav sācies. Skartās pozīcijas ir "
|
||||
"Šī pasākuma biļešu pārdošanas periods vēl nav sācies. Skartās pozīcijas ir "
|
||||
"noņemtas no jūsu groza."
|
||||
|
||||
#: pretix/base/services/cart.py:141 pretix/base/services/orders.py:182
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "The presale period for one of the events in your cart has ended. The "
|
||||
#| "affected positions have been removed from your cart."
|
||||
msgid ""
|
||||
"The booking period for one of the events in your cart has ended. The "
|
||||
"affected positions have been removed from your cart."
|
||||
msgstr ""
|
||||
"Ir beidzies iepriekšpārdošanas periods vienam no jūsu grozā esošajiem "
|
||||
"pasākumiem. Skartās pozīcijas ir noņemtas no jūsu groza."
|
||||
"Ir beidzies biļešu pārdošanas periods vienam no jūsu grozā esošajiem "
|
||||
"pasākumiem. Skartās pozīcijas ir izņemtas no jūsu groza."
|
||||
|
||||
#: pretix/base/services/cart.py:143
|
||||
#, fuzzy
|
||||
@@ -7497,10 +7465,8 @@ msgid "number of entries before {datetime}"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/services/checkin.py:307
|
||||
#, fuzzy
|
||||
#| msgid "Weekend day"
|
||||
msgid "week day"
|
||||
msgstr "Nedēļas nogales diena"
|
||||
msgstr "Nedēļas diena"
|
||||
|
||||
#: pretix/base/services/checkin.py:343 pretix/control/forms/filter.py:1130
|
||||
msgid "Monday"
|
||||
@@ -7558,17 +7524,14 @@ msgid "This ticket has been blocked."
|
||||
msgstr "Šī biļete jau ir izmantota."
|
||||
|
||||
#: pretix/base/services/checkin.py:882 pretix/base/services/checkin.py:886
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgctxt "subevent"
|
||||
#| msgid "This voucher is not valid for this event date."
|
||||
#, python-brace-format
|
||||
msgid "This ticket is only valid after {datetime}."
|
||||
msgstr "Šis kupons nav derīgs šajā pasākuma datumā."
|
||||
msgstr "Šis kupons ir derīgs tikai pēc {datetime}."
|
||||
|
||||
#: pretix/base/services/checkin.py:896 pretix/base/services/checkin.py:900
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "This ticket has already been redeemed."
|
||||
#, python-brace-format
|
||||
msgid "This ticket was only valid before {datetime}."
|
||||
msgstr "Šī biļete jau ir izmantota."
|
||||
msgstr "Šī biļete bija derīga tika līdz {datetime}."
|
||||
|
||||
#: pretix/base/services/checkin.py:931
|
||||
msgid "This order position has an invalid product for this check-in list."
|
||||
@@ -7865,10 +7828,8 @@ msgstr[2] ""
|
||||
"no jūsu groza noņēmām liekos vienumus."
|
||||
|
||||
#: pretix/base/services/orders.py:155
|
||||
#, fuzzy
|
||||
#| msgid "The presale period has ended."
|
||||
msgid "The booking period has ended."
|
||||
msgstr "Iepriekšpārdošanas periods ir beidzies."
|
||||
msgstr "Biļešu pārdošanas periods ir beidzies."
|
||||
|
||||
#: pretix/base/services/orders.py:161
|
||||
msgid ""
|
||||
@@ -7918,16 +7879,12 @@ msgstr ""
|
||||
"kods."
|
||||
|
||||
#: pretix/base/services/orders.py:178
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "The presale period for one of the events in your cart has not yet "
|
||||
#| "started. The affected positions have been removed from your cart."
|
||||
msgid ""
|
||||
"The booking period for one of the events in your cart has not yet started. "
|
||||
"The affected positions have been removed from your cart."
|
||||
msgstr ""
|
||||
"Vienam no jūsu grozā esošajiem pasākumiem vēl nav sācies pārdošanas laika "
|
||||
"posms. Skartās pozīcijas ir noņemtas no jūsu groza."
|
||||
"Vienam no jūsu grozā esošajiem pasākumiem vēl nav sācies biļešu pārdošanas "
|
||||
"periods. Skartās pozīcijas ir izņemtas no jūsu groza."
|
||||
|
||||
#: pretix/base/services/orders.py:185
|
||||
msgid ""
|
||||
@@ -7946,16 +7903,11 @@ msgstr ""
|
||||
"sēdvietām, tāpēc mēs noņēmām pozīciju no jūsu groza."
|
||||
|
||||
#: pretix/base/services/orders.py:202
|
||||
#, fuzzy, python-format
|
||||
#| msgid ""
|
||||
#| "This order can not be canceled since the gift card {card} purchased in "
|
||||
#| "this order has already been redeemed."
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You cannot remove the position %(addon)s since it has already been checked "
|
||||
"in."
|
||||
msgstr ""
|
||||
"Šo pasūtījumu nevar atcelt, jo dāvanu karte {card}, kas iegādāta šajā "
|
||||
"pasūtījumā, jau ir izmantota."
|
||||
msgstr "Pozīciju %(addon)s nevar atcelt, jo tas jau ir iečekots."
|
||||
|
||||
#: pretix/base/services/orders.py:218
|
||||
#, fuzzy
|
||||
@@ -8567,10 +8519,9 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/settings.py:682 pretix/base/settings.py:704
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Please do not use special characters in names."
|
||||
#, python-brace-format
|
||||
msgid "Please only use the characters {allowed} in this field."
|
||||
msgstr "Lūdzu neizmantojiet speciālās rakstzīmes vārdos."
|
||||
msgstr "Lūdzu izmantojiet tikai atļautās rakstzīmes ({allowed})."
|
||||
|
||||
#: pretix/base/settings.py:695
|
||||
msgid "Invoice number prefix for cancellations"
|
||||
@@ -8747,7 +8698,7 @@ msgstr ""
|
||||
|
||||
#: pretix/base/settings.py:996
|
||||
msgid "Accept late payments"
|
||||
msgstr ""
|
||||
msgstr "Pieņemt kavētus maksājumus"
|
||||
|
||||
#: pretix/base/settings.py:997
|
||||
msgid ""
|
||||
@@ -9963,7 +9914,7 @@ msgstr ""
|
||||
"\n"
|
||||
"{voucher_list}\n"
|
||||
"\n"
|
||||
"Jūs tos variet izmantot mūsu biļešu e-veikalā:\n"
|
||||
"Jūs tos varat izmantot mūsu biļešu e-veikalā:\n"
|
||||
"\n"
|
||||
"{url}\n"
|
||||
"\n"
|
||||
@@ -10063,7 +10014,7 @@ msgstr ""
|
||||
"\n"
|
||||
"{voucher_list}\n"
|
||||
"\n"
|
||||
"Jūs tos variet izmantot mūsu biļešu e-veikalā:\n"
|
||||
"Jūs tos varat izmantot mūsu biļešu e-veikalā:\n"
|
||||
"\n"
|
||||
"{url}\n"
|
||||
"\n"
|
||||
@@ -10799,7 +10750,7 @@ msgstr "Jums nav piekļuves šai lapai."
|
||||
#: pretix/control/templates/pretixcontrol/user/staff_session_start.html:4
|
||||
#: pretix/control/templates/pretixcontrol/user/staff_session_start.html:6
|
||||
msgid "Admin mode"
|
||||
msgstr ""
|
||||
msgstr "Admin režīms"
|
||||
|
||||
#: pretix/base/templates/404.html:4 pretix/base/templates/404.html:8
|
||||
msgid "Not found"
|
||||
@@ -10860,7 +10811,7 @@ msgstr ""
|
||||
#: pretix/base/templates/pretixbase/email/email_footer.html:3
|
||||
#, python-format
|
||||
msgid "powered by <a %(a_attr)s>pretix</a>"
|
||||
msgstr ""
|
||||
msgstr "darbina <a %(a_attr)s>pretix</a>"
|
||||
|
||||
#: pretix/base/templates/pretixbase/email/export_failed.txt:2
|
||||
#, fuzzy
|
||||
@@ -10869,10 +10820,8 @@ msgid "Your export failed."
|
||||
msgstr "Exportētie faili"
|
||||
|
||||
#: pretix/base/templates/pretixbase/email/export_failed.txt:4
|
||||
#, fuzzy
|
||||
#| msgid "Refund reason"
|
||||
msgid "Reason:"
|
||||
msgstr "Naudas atmaksāšanas iemesls"
|
||||
msgstr "Iemesls:"
|
||||
|
||||
#: pretix/base/templates/pretixbase/email/export_failed.txt:7
|
||||
msgid "If your export fails five times in a row, it will no longer be sent."
|
||||
@@ -12400,7 +12349,7 @@ msgstr "Sākuma datums līdz"
|
||||
#: pretix/control/forms/filter.py:1888 pretix/control/forms/filter.py:1891
|
||||
#: pretix/control/templates/pretixcontrol/users/index.html:52
|
||||
msgid "Administrator"
|
||||
msgstr ""
|
||||
msgstr "Administrators"
|
||||
|
||||
#: pretix/control/forms/filter.py:1892
|
||||
msgid "No administrator"
|
||||
@@ -12547,7 +12496,7 @@ msgstr "Deaktivizētās ierīces"
|
||||
|
||||
#: pretix/control/forms/global_settings.py:60
|
||||
msgid "Additional footer text"
|
||||
msgstr "Papildus lapas galvenes (footer) teksts"
|
||||
msgstr "Papildu teksts kājenei"
|
||||
|
||||
#: pretix/control/forms/global_settings.py:61
|
||||
msgid "Will be included as additional text in the footer, site-wide."
|
||||
@@ -15293,7 +15242,7 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/user/history.html:10
|
||||
#: pretix/control/templates/pretixcontrol/user/settings.html:70
|
||||
msgid "Account history"
|
||||
msgstr ""
|
||||
msgstr "Konta vēsture"
|
||||
|
||||
#: pretix/control/navigation.py:415
|
||||
msgid "All users"
|
||||
@@ -15303,7 +15252,7 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/user/staff_session_list.html:5
|
||||
#: pretix/control/templates/pretixcontrol/user/staff_session_list.html:7
|
||||
msgid "Admin sessions"
|
||||
msgstr ""
|
||||
msgstr "Admin sesijas"
|
||||
|
||||
#: pretix/control/navigation.py:427
|
||||
#: pretix/control/templates/pretixcontrol/global_settings_base.html:5
|
||||
@@ -15390,7 +15339,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/invite.html:7
|
||||
msgid "Accept an invitation"
|
||||
msgstr ""
|
||||
msgstr "Pieņemt ielūgumu"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/invite.html:10
|
||||
#, python-format
|
||||
@@ -15654,7 +15603,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/base.html:373
|
||||
msgid "Read more"
|
||||
msgstr ""
|
||||
msgstr "Lasīt vairāk"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/base.html:390
|
||||
msgid ""
|
||||
@@ -16353,7 +16302,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/dashboard.html:15
|
||||
msgid "Your upcoming events"
|
||||
msgstr ""
|
||||
msgstr "Jūsu gaidāmie pasākumi"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/dashboard.html:20
|
||||
#: pretix/control/templates/pretixcontrol/events/create_base.html:4
|
||||
@@ -16367,7 +16316,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/dashboard.html:39
|
||||
msgid "View all upcoming events"
|
||||
msgstr ""
|
||||
msgstr "Skatīt visus gaidāmos pasākumus"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/dashboard.html:44
|
||||
msgid "Your most recent events"
|
||||
@@ -16375,7 +16324,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/dashboard.html:60
|
||||
msgid "View all recent events"
|
||||
msgstr ""
|
||||
msgstr "Skatīt visus nesenos pasākumus"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/dashboard.html:65
|
||||
msgid "Your event series"
|
||||
@@ -16387,7 +16336,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/dashboard.html:86
|
||||
msgid "Other features"
|
||||
msgstr ""
|
||||
msgstr "Citas iespējas"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/email/email_setup.txt:1
|
||||
#, python-format
|
||||
@@ -16421,6 +16370,15 @@ msgid ""
|
||||
"Best regards, \n"
|
||||
"Your pretix team\n"
|
||||
msgstr ""
|
||||
"Sveicināti!\n"
|
||||
"\n"
|
||||
"Jūs pieprasījāt paroles maiņu. Lūdzu, dodieties uz šo lapu, lai atstatītu "
|
||||
"paroli:\n"
|
||||
"\n"
|
||||
"%(url)s\n"
|
||||
"\n"
|
||||
"Ar cieņu\n"
|
||||
"pretix komanda\n"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/email/invitation.txt:1
|
||||
#, python-format
|
||||
@@ -16898,7 +16856,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/event/invoicing.html:26
|
||||
msgid "Address form"
|
||||
msgstr ""
|
||||
msgstr "Adreses forma"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/event/invoicing.html:38
|
||||
msgid "Issuer details"
|
||||
@@ -18292,7 +18250,7 @@ msgstr "Jauns variants"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/item/include_variations.html:215
|
||||
msgid "Add a new variation"
|
||||
msgstr ""
|
||||
msgstr "Pievienot jaunu variāciju"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/item/index.html:153
|
||||
msgid "Availability"
|
||||
@@ -18449,13 +18407,11 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/discount_delete.html:21
|
||||
#, fuzzy, python-format
|
||||
#| msgid ""
|
||||
#| "Are you sure you want to delete the rule <strong>%(subject)s</strong>?"
|
||||
#, python-format
|
||||
msgid "Are you sure you want to delete the discount <strong>%(name)s</strong>?"
|
||||
msgstr ""
|
||||
"Vai esat pārliecināts/-a, ka vēlaties dzēst noteikumu <strong>%(subject)s</"
|
||||
"strong>?"
|
||||
"Vai esat pārliecināts/-a, ka vēlaties dzēst atlaidi "
|
||||
"<strong>%(name)s</strong>?"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/discount_delete.html:25
|
||||
#, python-format
|
||||
@@ -19472,10 +19428,9 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html:450
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:107
|
||||
#, fuzzy, python-format
|
||||
#| msgid "Admission: %(datetime)s"
|
||||
#, python-format
|
||||
msgid "Valid %(datetime_range)s"
|
||||
msgstr "Ieeja: %(datetime)s"
|
||||
msgstr "Korekts %(datetime_range)s"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html:496
|
||||
msgid "Ticket page"
|
||||
@@ -20452,7 +20407,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/orders/refunds.html:74
|
||||
msgid "Actions"
|
||||
msgstr ""
|
||||
msgstr "Darbības"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/orders/search.html:7
|
||||
#: pretix/control/templates/pretixcontrol/orders/search.html:9
|
||||
@@ -20969,10 +20924,8 @@ msgid "Remove"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/giftcard_acceptance_list.html:66
|
||||
#, fuzzy
|
||||
#| msgid "Accept anyway"
|
||||
msgid "Accept"
|
||||
msgstr "Piekrist tik un tā"
|
||||
msgstr "Piekrist"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/giftcard_acceptance_list.html:69
|
||||
msgid "Decline"
|
||||
@@ -21324,7 +21277,7 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/organizers/team_members.html:81
|
||||
#: pretix/control/templates/pretixcontrol/organizers/team_members.html:122
|
||||
msgid "Add"
|
||||
msgstr ""
|
||||
msgstr "Pievienot"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/team_members.html:88
|
||||
msgid "API tokens"
|
||||
@@ -29606,17 +29559,14 @@ msgstr "Citi datumi"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:142
|
||||
#: pretix/presale/views/waiting.py:100 pretix/presale/views/widget.py:708
|
||||
#, fuzzy
|
||||
#| msgid "The presale period for this event is over."
|
||||
msgid "The booking period for this event is over."
|
||||
msgstr "Šī pasākuma iepriekšpārdošanas periods ir beidzies."
|
||||
msgstr "Pasākuma biļešu pārdošanas periods ir beidzies."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:150
|
||||
#: pretix/presale/views/widget.py:710
|
||||
#, fuzzy, python-format
|
||||
#| msgid "The presale for this event will start on %(date)s at %(time)s."
|
||||
#, python-format
|
||||
msgid "The booking period for this event will start on %(date)s at %(time)s."
|
||||
msgstr "Šī pasākuma iepriekšpārdošana sāksies %(date)s plkst. %(time)s."
|
||||
msgstr "Šī pasākuma biļešu pārdošanas periods sāksies %(date)s plkst. %(time)s."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:172
|
||||
#: pretix/presale/templates/pretixpresale/event/seatingplan.html:23
|
||||
@@ -30614,7 +30564,7 @@ msgstr "Netika atrasti publiski gaidāmi pasākumi."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/organizers/index.html:143
|
||||
msgid "Show past events"
|
||||
msgstr "Rādīt pagātnes pasākumus"
|
||||
msgstr "Rādīt jau notikušus pasākumus"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/pagination.html:14
|
||||
#, python-format
|
||||
@@ -30687,10 +30637,8 @@ msgid "Your cart is empty"
|
||||
msgstr "Jūsu grozs ir tukšs"
|
||||
|
||||
#: pretix/presale/views/checkout.py:59
|
||||
#, fuzzy
|
||||
#| msgid "The presale for this event is over or has not yet started."
|
||||
msgid "The booking period for this event is over or has not yet started."
|
||||
msgstr "Šī pasākuma iepriekšpārdošana ir beigusies vai vēl nav sākusies."
|
||||
msgstr "Šī pasākuma biļešu pārdošanas periods ir beidzies vai vēl nav sācies."
|
||||
|
||||
#: pretix/presale/views/customer.py:247
|
||||
msgid ""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-10-18 07:26+0000\n"
|
||||
"PO-Revision-Date: 2022-06-20 02:00+0000\n"
|
||||
"PO-Revision-Date: 2023-11-02 13:02+0000\n"
|
||||
"Last-Translator: fyksen <fredrik@fyksen.me>\n"
|
||||
"Language-Team: Norwegian Bokmål <https://translate.pretix.eu/projects/pretix/"
|
||||
"pretix-js/nb_NO/>\n"
|
||||
@@ -17,13 +17,13 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.12.2\n"
|
||||
"X-Generator: Weblate 5.1.1\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:68
|
||||
msgid "Marked as paid"
|
||||
msgstr "Sett som betalt"
|
||||
msgstr "Merkert som betalt"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:76
|
||||
msgid "Comment:"
|
||||
@@ -209,7 +209,7 @@ msgstr "Inngang"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:39
|
||||
msgid "Exit"
|
||||
msgstr "Utgang"
|
||||
msgstr "Avslutt"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:40
|
||||
msgid "Scan a ticket or search and press return…"
|
||||
@@ -230,11 +230,11 @@ msgstr "Ubetalt"
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:44
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:45
|
||||
msgid "Canceled"
|
||||
msgstr "Kansellert"
|
||||
msgstr "Avlyst"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:46
|
||||
msgid "Redeemed"
|
||||
msgstr "Løst inn"
|
||||
msgstr "Innløst"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:47
|
||||
msgid "Cancel"
|
||||
@@ -243,7 +243,7 @@ msgstr "Avbryt"
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:49
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58
|
||||
msgid "Ticket not paid"
|
||||
msgstr "Billett ikke betalt"
|
||||
msgstr "Billetten er ikke betalt."
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:50
|
||||
msgid "This ticket is not yet paid. Do you want to continue anyways?"
|
||||
@@ -251,7 +251,7 @@ msgstr "Denne billetten er ikke betalt. Vil du fortsette likevel?"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
|
||||
msgid "Additional information required"
|
||||
msgstr "Ekstra informasjon kreves"
|
||||
msgstr "pretix account invitation"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
|
||||
msgid "Valid ticket"
|
||||
@@ -263,11 +263,11 @@ msgstr "Utgang registrert"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:54
|
||||
msgid "Ticket already used"
|
||||
msgstr "Billett allerede benyttet"
|
||||
msgstr "Billetten er allerede brukt."
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:55
|
||||
msgid "Information required"
|
||||
msgstr "Informasjon trengs"
|
||||
msgstr "Informasjon påkrevd"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56
|
||||
msgid "Unknown ticket"
|
||||
@@ -286,16 +286,12 @@ msgid "Ticket code revoked/changed"
|
||||
msgstr "Billettkode tilbakekalt/endret"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
|
||||
#, fuzzy
|
||||
#| msgid "Ticket not paid"
|
||||
msgid "Ticket blocked"
|
||||
msgstr "Billett ikke betalt"
|
||||
msgstr "Billett blokkert"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
|
||||
#, fuzzy
|
||||
#| msgid "Ticket not paid"
|
||||
msgid "Ticket not valid at this time"
|
||||
msgstr "Billett ikke betalt"
|
||||
msgstr "Billetten er ikke gyldig på dette tidspunktet"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
|
||||
msgid "Order canceled"
|
||||
@@ -303,7 +299,7 @@ msgstr "Ordre kansellert"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
|
||||
msgid "Ticket code is ambiguous on list"
|
||||
msgstr ""
|
||||
msgstr "Billettkoden er tvetydig på listen"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
|
||||
msgid "Checked-in Tickets"
|
||||
@@ -423,7 +419,7 @@ msgstr "Produkt variasjon"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:101
|
||||
msgid "Gate"
|
||||
msgstr ""
|
||||
msgstr "Port"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:105
|
||||
msgid "Current date and time"
|
||||
@@ -442,16 +438,12 @@ msgid "Number of previous entries since midnight"
|
||||
msgstr "Antall tidligere oppføringer siden midnatt"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:121
|
||||
#, fuzzy
|
||||
#| msgid "Number of previous entries"
|
||||
msgid "Number of previous entries since"
|
||||
msgstr "Antall tidligere oppføringer"
|
||||
msgstr "Antall tidligere oppføringer siden"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:125
|
||||
#, fuzzy
|
||||
#| msgid "Number of previous entries"
|
||||
msgid "Number of previous entries before"
|
||||
msgstr "Antall tidligere oppføringer"
|
||||
msgstr "Antall tidligere oppføringer før"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:129
|
||||
msgid "Number of days with a previous entry"
|
||||
@@ -507,7 +499,7 @@ msgstr "minutter"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:174
|
||||
msgid "Duplicate"
|
||||
msgstr ""
|
||||
msgstr "Duplikat"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:72
|
||||
msgid "Check-in QR"
|
||||
@@ -689,44 +681,39 @@ msgid "Your local time:"
|
||||
msgstr "Din lokale tid:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||
#, fuzzy
|
||||
#| msgid "Apple Pay"
|
||||
msgid "Google Pay"
|
||||
msgstr "Apple Pay"
|
||||
msgstr "Google Pay"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:16
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
msgstr ""
|
||||
msgstr "Antall"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||
msgctxt "widget"
|
||||
msgid "Decrease quantity"
|
||||
msgstr ""
|
||||
msgstr "Begrenset antall"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
||||
msgctxt "widget"
|
||||
msgid "Increase quantity"
|
||||
msgstr ""
|
||||
msgstr "Øk antall"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||
msgctxt "widget"
|
||||
msgid "Price"
|
||||
msgstr ""
|
||||
msgstr "Pris"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
||||
#, fuzzy
|
||||
#| msgid "Selected only"
|
||||
msgctxt "widget"
|
||||
msgid "Select"
|
||||
msgstr "Kun valgte"
|
||||
msgstr "Velg"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
||||
#, fuzzy, javascript-format
|
||||
#| msgid "Selected only"
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select %s"
|
||||
msgstr "Kun valgte"
|
||||
msgstr "Velg%s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:22
|
||||
#, fuzzy, javascript-format
|
||||
@@ -937,6 +924,9 @@ msgid ""
|
||||
"add yourself to the waiting list. We will then notify if seats are available "
|
||||
"again."
|
||||
msgstr ""
|
||||
"Noen eller alle billettkategorier er for øyeblikket utsolgt. Hvis du ønsker, "
|
||||
"kan du legge deg til på ventelisten. Vi vil da gi deg beskjed hvis det blir "
|
||||
"ledige seter igjen."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgctxt "widget"
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-10-26 12:47+0000\n"
|
||||
"PO-Revision-Date: 2023-07-04 06:00+0000\n"
|
||||
"Last-Translator: Maciej Szymczak <maciej+github@szymczak.at>\n"
|
||||
"PO-Revision-Date: 2023-11-06 09:00+0000\n"
|
||||
"Last-Translator: Fast128 <fast128@post.pl>\n"
|
||||
"Language-Team: Polish <https://translate.pretix.eu/projects/pretix/pretix/pl/"
|
||||
">\n"
|
||||
"Language: pl\n"
|
||||
@@ -18,7 +18,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
|
||||
"|| n%100>=20) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 4.17\n"
|
||||
"X-Generator: Weblate 5.1.1\n"
|
||||
|
||||
#: pretix/_base_settings.py:78
|
||||
msgid "English"
|
||||
@@ -78,7 +78,7 @@ msgstr "Grecki"
|
||||
|
||||
#: pretix/_base_settings.py:92
|
||||
msgid "Indonesian"
|
||||
msgstr ""
|
||||
msgstr "Indonezyjski"
|
||||
|
||||
#: pretix/_base_settings.py:93
|
||||
msgid "Italian"
|
||||
@@ -333,10 +333,8 @@ msgstr ""
|
||||
|
||||
#: pretix/api/serializers/organizer.py:102
|
||||
#: pretix/control/forms/organizer.py:798 pretix/presale/forms/customer.py:439
|
||||
#, fuzzy
|
||||
#| msgid "A voucher with this code already exists."
|
||||
msgid "An account with this email address is already registered."
|
||||
msgstr "Voucher o tym kodzie już istnieje."
|
||||
msgstr "Konto o tym adresie e-mail już istnieje."
|
||||
|
||||
#: pretix/api/serializers/organizer.py:205
|
||||
#: pretix/control/forms/organizer.py:647
|
||||
@@ -551,23 +549,16 @@ msgid "Waiting list entry received voucher"
|
||||
msgstr "Osoba z listy oczekujących dostała voucher"
|
||||
|
||||
#: pretix/api/webhooks.py:370
|
||||
#, fuzzy
|
||||
#| msgctxt "refund_source"
|
||||
#| msgid "Customer"
|
||||
msgid "Customer account created"
|
||||
msgstr "Klient"
|
||||
msgstr "Stworzono konto użytkownika"
|
||||
|
||||
#: pretix/api/webhooks.py:374
|
||||
#, fuzzy
|
||||
#| msgid "Account information changed"
|
||||
msgid "Customer account changed"
|
||||
msgstr "Zmiana informacji konta"
|
||||
msgstr "Zmieniono konto użytkownika"
|
||||
|
||||
#: pretix/api/webhooks.py:378
|
||||
#, fuzzy
|
||||
#| msgid "This product can only be bought using a voucher."
|
||||
msgid "Customer account anonymized"
|
||||
msgstr "Produkt może być kupiony tylko przy użyciu vouchera."
|
||||
msgstr "Konto użytkownika zanonimizowane"
|
||||
|
||||
#: pretix/base/addressvalidation.py:100 pretix/base/addressvalidation.py:103
|
||||
#: pretix/base/addressvalidation.py:108 pretix/base/forms/questions.py:934
|
||||
@@ -2947,7 +2938,7 @@ msgstr "Wprowadzone obecne hasło jest nieprawidłowe."
|
||||
|
||||
#: pretix/base/forms/user.py:58
|
||||
msgid "Please choose a password different to your current one."
|
||||
msgstr ""
|
||||
msgstr "Nowe hasło nie może być identyczne jak obecne."
|
||||
|
||||
#: pretix/base/forms/user.py:63 pretix/presale/forms/customer.py:373
|
||||
#: pretix/presale/forms/customer.py:442
|
||||
@@ -2989,10 +2980,8 @@ msgid "Smartphone with the Authenticator application"
|
||||
msgstr "Smartfon z aplikacją Authenticator"
|
||||
|
||||
#: pretix/base/forms/user.py:178
|
||||
#, fuzzy
|
||||
#| msgid "U2F-compatible hardware token (e.g. Yubikey)"
|
||||
msgid "WebAuthn-compatible hardware token (e.g. Yubikey)"
|
||||
msgstr "Token hardware'owy kompatybilny z U2F (np. Yubikey)"
|
||||
msgstr "Token hardware'owy kompatybilny z WebAuthn (np. Yubikey)"
|
||||
|
||||
#: pretix/base/forms/validators.py:62
|
||||
msgid ""
|
||||
@@ -3016,11 +3005,9 @@ msgstr "Przedsprzedaż nierozpoczęta"
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Invalid placeholder(s): %(value)s"
|
||||
msgid "Available placeholders: {list}"
|
||||
msgstr "Nieprawidłowy(e) symbol(e) zastępstwa: %(value)s"
|
||||
msgstr "Dostępne symbole zastępstwa: %(value)"
|
||||
|
||||
#: pretix/base/forms/widgets.py:230 pretix/base/forms/widgets.py:235
|
||||
#, fuzzy
|
||||
#| msgid "Business customer"
|
||||
msgid "Business or institutional customer"
|
||||
msgstr "Klient firmowy"
|
||||
|
||||
@@ -3112,12 +3099,9 @@ msgid "Customer reference: {reference}"
|
||||
msgstr "Adnotacja klienta: {reference}"
|
||||
|
||||
#: pretix/base/invoice.py:568
|
||||
#, fuzzy
|
||||
#| msgctxt "refund_source"
|
||||
#| msgid "Customer"
|
||||
msgctxt "invoice"
|
||||
msgid "Customer VAT ID"
|
||||
msgstr "Klient"
|
||||
msgstr "Numer NIP Klienta"
|
||||
|
||||
#: pretix/base/invoice.py:575
|
||||
msgctxt "invoice"
|
||||
@@ -3125,11 +3109,9 @@ msgid "Beneficiary"
|
||||
msgstr "Beneficjent"
|
||||
|
||||
#: pretix/base/invoice.py:596
|
||||
#, fuzzy
|
||||
#| msgid "Invoice"
|
||||
msgctxt "invoice"
|
||||
msgid "Tax Invoice"
|
||||
msgstr "Faktura"
|
||||
msgstr "Faktura VAT"
|
||||
|
||||
#: pretix/base/invoice.py:597
|
||||
msgctxt "invoice"
|
||||
@@ -3177,14 +3159,14 @@ msgstr "Wartość"
|
||||
#, python-brace-format
|
||||
msgctxt "invoice"
|
||||
msgid "Single price: {net_price} net / {gross_price} gross"
|
||||
msgstr ""
|
||||
msgstr "Cena jednostkowa: {net_price} netto / {gross_price} brutto"
|
||||
|
||||
#: pretix/base/invoice.py:659
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Original price"
|
||||
msgctxt "invoice"
|
||||
msgid "Single price: {price}"
|
||||
msgstr "Cena oryginalna"
|
||||
msgstr "Cena jednostkowa"
|
||||
|
||||
#: pretix/base/invoice.py:677 pretix/base/invoice.py:683
|
||||
msgctxt "invoice"
|
||||
@@ -3192,30 +3174,24 @@ msgid "Invoice total"
|
||||
msgstr "Razem"
|
||||
|
||||
#: pretix/base/invoice.py:693
|
||||
#, fuzzy
|
||||
#| msgid "Only successful payments"
|
||||
msgctxt "invoice"
|
||||
msgid "Received payments"
|
||||
msgstr "Tylko pomyślne płatności"
|
||||
msgstr "Otrzymane płatności"
|
||||
|
||||
#: pretix/base/invoice.py:698
|
||||
msgctxt "invoice"
|
||||
msgid "Outstanding payments"
|
||||
msgstr ""
|
||||
msgstr "Zaległe płatności"
|
||||
|
||||
#: pretix/base/invoice.py:715
|
||||
#, fuzzy
|
||||
#| msgid "Gift card"
|
||||
msgctxt "invoice"
|
||||
msgid "Paid by gift card"
|
||||
msgstr "Karta prezentowa"
|
||||
msgstr "Zapłacono kartą prezentową"
|
||||
|
||||
#: pretix/base/invoice.py:720
|
||||
#, fuzzy
|
||||
#| msgid "Question option"
|
||||
msgctxt "invoice"
|
||||
msgid "Remaining amount"
|
||||
msgstr "Opcja pytania"
|
||||
msgstr "Pozostała kwota"
|
||||
|
||||
#: pretix/base/invoice.py:769
|
||||
msgctxt "invoice"
|
||||
@@ -3266,11 +3242,8 @@ msgstr ""
|
||||
"Centrylny w dniu {date}, suma faktury wynosi: {total}.."
|
||||
|
||||
#: pretix/base/invoice.py:858
|
||||
#, fuzzy
|
||||
#| msgctxt "invoice"
|
||||
#| msgid "Classic renderer (pretix 1.0)"
|
||||
msgid "Modern Invoice Renderer (pretix 2.7)"
|
||||
msgstr "Klasyczny renderer (pretix 1.0)"
|
||||
msgstr "Modern Invoice Renderer (pretix 2.7)"
|
||||
|
||||
#: pretix/base/invoice.py:947
|
||||
#, fuzzy
|
||||
@@ -3281,12 +3254,12 @@ msgstr "Proszę wpisać to samo hasło dwukrotnie"
|
||||
|
||||
#: pretix/base/media.py:61
|
||||
msgid "Barcode / QR-Code"
|
||||
msgstr ""
|
||||
msgstr "Kod kreskowy / Kod QR"
|
||||
|
||||
#: pretix/base/media.py:77
|
||||
#: pretix/control/templates/pretixcontrol/organizers/edit.html:237
|
||||
msgid "NFC UID-based"
|
||||
msgstr ""
|
||||
msgstr "NFC na bazie UID"
|
||||
|
||||
#: pretix/base/migrations/0077_auto_20171124_1629.py:33
|
||||
#: pretix/base/migrations/0077_auto_20171124_1629_squashed_0088_auto_20180328_1217.py:35
|
||||
@@ -3306,10 +3279,8 @@ msgid "Date joined"
|
||||
msgstr "Data dołączenia"
|
||||
|
||||
#: pretix/base/models/auth.py:256
|
||||
#, fuzzy
|
||||
#| msgid "Repeat new password"
|
||||
msgid "Force user to select a new password"
|
||||
msgstr "Powtórz nowe hasło"
|
||||
msgstr "Wymuś wybranie nowego hasła użytkownika"
|
||||
|
||||
#: pretix/base/models/auth.py:266
|
||||
msgid "Two-factor authentication is required to log in"
|
||||
@@ -3369,18 +3340,20 @@ msgstr ""
|
||||
#: pretix/base/models/checkin.py:66
|
||||
msgctxt "checkin"
|
||||
msgid "Ignore check-ins on this list in statistics"
|
||||
msgstr ""
|
||||
msgstr "Ignoruj zameldowania obecne na tej liście w statystykach"
|
||||
|
||||
#: pretix/base/models/checkin.py:70
|
||||
msgctxt "checkin"
|
||||
msgid "Tickets with a check-in on this list should be considered \"used\""
|
||||
msgstr ""
|
||||
msgstr "Zameldowane bilety z tej listy powinny być uważane za \"użyte\""
|
||||
|
||||
#: pretix/base/models/checkin.py:71
|
||||
msgid ""
|
||||
"This is relevant in various situations, e.g. for deciding if a ticket can "
|
||||
"still be canceled by the customer."
|
||||
msgstr ""
|
||||
"To istotne w wielu sytuacjach, np żeby zdecydować czy bilet może być "
|
||||
"anulowany przez klienta."
|
||||
|
||||
#: pretix/base/models/checkin.py:75
|
||||
msgctxt "checkin"
|
||||
@@ -3399,6 +3372,7 @@ msgstr ""
|
||||
#: pretix/base/models/checkin.py:80
|
||||
msgid "Allow checking in add-on tickets by scanning the main ticket"
|
||||
msgstr ""
|
||||
"Pozwól na zameldowanie dodatkowych biletów przez skanowanie głównego biletu"
|
||||
|
||||
#: pretix/base/models/checkin.py:82
|
||||
msgid ""
|
||||
@@ -3410,36 +3384,37 @@ msgstr ""
|
||||
#: pretix/base/models/checkin.py:86 pretix/control/navigation.py:626
|
||||
#: pretix/control/templates/pretixcontrol/organizers/gates.html:5
|
||||
msgid "Gates"
|
||||
msgstr ""
|
||||
msgstr "Bramki"
|
||||
|
||||
#: pretix/base/models/checkin.py:87
|
||||
msgid ""
|
||||
"Does not have any effect for the validation of tickets, only for the "
|
||||
"automatic configuration of check-in devices."
|
||||
msgstr ""
|
||||
"Nie wpływa na sprawdzenie ważności biletu, tylko na automatyczną "
|
||||
"konfigurację urządzeń do sprawdzania biletów."
|
||||
|
||||
#: pretix/base/models/checkin.py:91
|
||||
msgid "Allow re-entering after an exit scan"
|
||||
msgstr ""
|
||||
msgstr "Pozwól na ponowne wejście bo zeskanowaniu na wyjściu"
|
||||
|
||||
#: pretix/base/models/checkin.py:95
|
||||
msgid "Allow multiple entries per ticket"
|
||||
msgstr ""
|
||||
msgstr "Pozwól na wielokrotne wejścia na ten sam bilet"
|
||||
|
||||
#: pretix/base/models/checkin.py:96
|
||||
msgid ""
|
||||
"Use this option to turn off warnings if a ticket is scanned a second time."
|
||||
msgstr ""
|
||||
"Użyj tej opcji by wyłączyć ostrzeżenie, że bilet jest skanowany ponownie."
|
||||
|
||||
#: pretix/base/models/checkin.py:100
|
||||
#, fuzzy
|
||||
#| msgid "Ticket checked in"
|
||||
msgid "Automatically check out everyone at"
|
||||
msgstr "Bilet zameldowany"
|
||||
msgstr "Automatycznie zamelduj wszystkich w"
|
||||
|
||||
#: pretix/base/models/checkin.py:106
|
||||
msgid "Sales channels to automatically check in"
|
||||
msgstr ""
|
||||
msgstr "Kanały sprzedaży do automatycznego sprawdzania biletów"
|
||||
|
||||
#: pretix/base/models/checkin.py:107
|
||||
msgid ""
|
||||
@@ -3450,18 +3425,16 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/models/checkin.py:340
|
||||
#, fuzzy
|
||||
#| msgid "Country"
|
||||
msgid "Entry"
|
||||
msgstr "Kraj"
|
||||
msgstr "Wejście"
|
||||
|
||||
#: pretix/base/models/checkin.py:341
|
||||
msgid "Exit"
|
||||
msgstr ""
|
||||
msgstr "Wyjście"
|
||||
|
||||
#: pretix/base/models/checkin.py:358
|
||||
msgid "Unknown ticket"
|
||||
msgstr ""
|
||||
msgstr "Bilet nieznany"
|
||||
|
||||
#: pretix/base/models/checkin.py:359
|
||||
msgid "Ticket not paid"
|
||||
@@ -3469,7 +3442,7 @@ msgstr "Bilet nie został opłacony"
|
||||
|
||||
#: pretix/base/models/checkin.py:360
|
||||
msgid "Forbidden by custom rule"
|
||||
msgstr ""
|
||||
msgstr "Zabronione przez niestandardową regułę"
|
||||
|
||||
#: pretix/base/models/checkin.py:361
|
||||
#, fuzzy
|
||||
@@ -3497,7 +3470,7 @@ msgstr "Przedsprzedaż nierozpoczęta"
|
||||
|
||||
#: pretix/base/models/checkin.py:365
|
||||
msgid "Ticket code is ambiguous on list"
|
||||
msgstr ""
|
||||
msgstr "Kod biletu jest niejednoznaczny na liście"
|
||||
|
||||
#: pretix/base/models/checkin.py:366
|
||||
#, fuzzy
|
||||
@@ -3518,14 +3491,12 @@ msgid "Ticket not valid at this time"
|
||||
msgstr "Produkt nie będzie sprzedawany przed podaną datą."
|
||||
|
||||
#: pretix/base/models/customers.py:55
|
||||
#, fuzzy
|
||||
#| msgid "Attendee name"
|
||||
msgid "Provider name"
|
||||
msgstr "Imię uczstnika"
|
||||
msgstr "Imię dostarczyciela"
|
||||
|
||||
#: pretix/base/models/customers.py:60
|
||||
msgid "Login button label"
|
||||
msgstr ""
|
||||
msgstr "Etykieta przycisku logowania"
|
||||
|
||||
#: pretix/base/models/customers.py:64
|
||||
#, fuzzy
|
||||
@@ -3547,6 +3518,8 @@ msgid ""
|
||||
"The identifier may only contain letters, numbers, dots, dashes, and "
|
||||
"underscores. It must start and end with a letter or number."
|
||||
msgstr ""
|
||||
"Identyfikator może zawierać jedynie litery, cyfry, kropki, średniki i "
|
||||
"podkreślniki. Może zaczynać się i kończyć literą, lub cyfrą."
|
||||
|
||||
#: pretix/base/models/customers.py:299 pretix/base/models/orders.py:1410
|
||||
#: pretix/base/models/orders.py:2989 pretix/base/settings.py:1093
|
||||
@@ -3562,40 +3535,34 @@ msgstr "Proszę wybrać kraj"
|
||||
#: pretix/base/models/customers.py:370
|
||||
msgctxt "openidconnect"
|
||||
msgid "Confidential"
|
||||
msgstr ""
|
||||
msgstr "Poufne"
|
||||
|
||||
#: pretix/base/models/customers.py:371
|
||||
msgctxt "openidconnect"
|
||||
msgid "Public"
|
||||
msgstr ""
|
||||
msgstr "Publiczne"
|
||||
|
||||
#: pretix/base/models/customers.py:377
|
||||
#, fuzzy
|
||||
#| msgid "Cart positions"
|
||||
msgctxt "openidconnect"
|
||||
msgid "Authorization code"
|
||||
msgstr "Pozycje wózka"
|
||||
msgstr "Kod autoryzacyjny"
|
||||
|
||||
#: pretix/base/models/customers.py:378
|
||||
msgctxt "openidconnect"
|
||||
msgid "Implicit"
|
||||
msgstr ""
|
||||
msgstr "Domniemany"
|
||||
|
||||
#: pretix/base/models/customers.py:382
|
||||
msgid "OpenID Connect access (required)"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/models/customers.py:383
|
||||
#, fuzzy
|
||||
#| msgid "Creation date"
|
||||
msgid "Profile data (name, addresses)"
|
||||
msgstr "Data stworzenia"
|
||||
msgstr "Dane profilu (nazwa, adresy)"
|
||||
|
||||
#: pretix/base/models/customers.py:403
|
||||
#, fuzzy
|
||||
#| msgid "Payment fee"
|
||||
msgid "Client type"
|
||||
msgstr "Prowizja płatności"
|
||||
msgstr "Typ klienta"
|
||||
|
||||
#: pretix/base/models/customers.py:406
|
||||
#, fuzzy
|
||||
@@ -3605,11 +3572,11 @@ msgstr "Typ urządzenia"
|
||||
|
||||
#: pretix/base/models/customers.py:417
|
||||
msgid "Allowed access scopes"
|
||||
msgstr ""
|
||||
msgstr "Zakres dozwolonego dostępu"
|
||||
|
||||
#: pretix/base/models/customers.py:418
|
||||
msgid "Separate multiple values with spaces"
|
||||
msgstr ""
|
||||
msgstr "Rozdziel wiele wartości spacją"
|
||||
|
||||
#: pretix/base/models/devices.py:71 pretix/base/models/items.py:1478
|
||||
msgid "Internal identifier"
|
||||
@@ -3626,7 +3593,7 @@ msgstr "Identyfikator użyty przy innym pytaniu."
|
||||
#: pretix/control/templates/pretixcontrol/organizers/gates.html:16
|
||||
#: pretix/plugins/checkinlists/exporters.py:741
|
||||
msgid "Gate"
|
||||
msgstr ""
|
||||
msgstr "Bramka"
|
||||
|
||||
#: pretix/base/models/devices.py:132
|
||||
#: pretix/control/templates/pretixcontrol/organizers/devices.html:83
|
||||
@@ -3640,7 +3607,7 @@ msgstr "Data inicjalizacji"
|
||||
#: pretix/base/models/discount.py:45
|
||||
msgctxt "subevent"
|
||||
msgid "Dates can be mixed without limitation"
|
||||
msgstr ""
|
||||
msgstr "Daty mogą być mieszane bez ograniczeń"
|
||||
|
||||
#: pretix/base/models/discount.py:46
|
||||
#, fuzzy
|
||||
@@ -3652,7 +3619,7 @@ msgstr "Produkt nie będzie sprzedawany przed podaną datą."
|
||||
#: pretix/base/models/discount.py:47
|
||||
msgctxt "subevent"
|
||||
msgid "Each matching product must be for a different date"
|
||||
msgstr ""
|
||||
msgstr "Każdy pasujący produkt musi być na inną datę"
|
||||
|
||||
#: pretix/base/models/discount.py:65 pretix/base/models/items.py:1010
|
||||
#: pretix/base/models/items.py:1287 pretix/base/models/items.py:1511
|
||||
@@ -3673,7 +3640,7 @@ msgstr "Wszystkie produkty (łącznie z nowo stworzonymi)"
|
||||
|
||||
#: pretix/base/models/discount.py:97
|
||||
msgid "Apply to specific products"
|
||||
msgstr ""
|
||||
msgstr "Zastosuj do wybranych produktów"
|
||||
|
||||
#: pretix/base/models/discount.py:102
|
||||
#, fuzzy
|
||||
@@ -3683,11 +3650,11 @@ msgstr "Ograniczenie do produktów"
|
||||
|
||||
#: pretix/base/models/discount.py:103 pretix/base/models/discount.py:158
|
||||
msgid "Discounts never apply to bundled products"
|
||||
msgstr ""
|
||||
msgstr "Zniżki nigdy nie obowiązuja dla zgrupowanych produktów"
|
||||
|
||||
#: pretix/base/models/discount.py:107 pretix/base/models/discount.py:162
|
||||
msgid "Ignore products discounted by a voucher"
|
||||
msgstr ""
|
||||
msgstr "Ignoruj produkty ze zniżką z kuponu"
|
||||
|
||||
#: pretix/base/models/discount.py:108
|
||||
msgid ""
|
||||
@@ -3696,14 +3663,18 @@ msgid ""
|
||||
"use a voucher only to e.g. unlock a hidden product or gain access to sold-"
|
||||
"out quota will still be considered."
|
||||
msgstr ""
|
||||
"Jeżeli ta opcja jest zaznaczona, produkty które już otrzymały zniżkę z "
|
||||
"vouchera nie będą kwalifikować się do tej zniżki. Podukty przy których użyto "
|
||||
"vouchera np po to by odblokować ukryty produkt, lub uzyskać dostęp do "
|
||||
"wyprzedanego produktu nadal będą kwalifikować się do zniżki."
|
||||
|
||||
#: pretix/base/models/discount.py:113
|
||||
msgid "Minimum number of matching products"
|
||||
msgstr ""
|
||||
msgstr "Minimalna ilość zgodnych produktów"
|
||||
|
||||
#: pretix/base/models/discount.py:117
|
||||
msgid "Minimum gross value of matching products"
|
||||
msgstr ""
|
||||
msgstr "Minimalna wartość brutto zgodnych produktów"
|
||||
|
||||
#: pretix/base/models/discount.py:125
|
||||
#, fuzzy
|
||||
@@ -3726,11 +3697,11 @@ msgstr "Ograniczenie do produktów"
|
||||
|
||||
#: pretix/base/models/discount.py:137
|
||||
msgid "Percentual discount on matching products"
|
||||
msgstr ""
|
||||
msgstr "Procentowa zniżka na zgodnych produktach"
|
||||
|
||||
#: pretix/base/models/discount.py:144
|
||||
msgid "Apply discount only to this number of matching products"
|
||||
msgstr ""
|
||||
msgstr "Zastosuj zniżkę tylko dla tej ilości zgodnych produktów"
|
||||
|
||||
#: pretix/base/models/discount.py:146
|
||||
msgid ""
|
||||
@@ -4375,7 +4346,7 @@ msgid ""
|
||||
"many times. If you keep the field empty or set it to 0, there is no special "
|
||||
"limit for this product."
|
||||
msgstr ""
|
||||
"Ten produkt może zostać zamówiony tylko jeśli wózek użytkownika zawiera go "
|
||||
"Ten produkt może zostać zamówiony tylko jeśli koszyk użytkownika zawiera go "
|
||||
"przynajmniej tyle razy. Pozostawienie pola pustego lub wprowadzenie 0 "
|
||||
"wyłącza ten limit."
|
||||
|
||||
@@ -5556,10 +5527,8 @@ msgstr ""
|
||||
"uprawnienie."
|
||||
|
||||
#: pretix/base/models/organizer.py:283
|
||||
#, fuzzy
|
||||
#| msgid "Internal comment"
|
||||
msgid "Can manage customer accounts"
|
||||
msgstr "Komentarz wewnętrzny"
|
||||
msgstr "Może zarządzać kontami użytkownikó∑"
|
||||
|
||||
#: pretix/base/models/organizer.py:287
|
||||
#, fuzzy
|
||||
@@ -7045,10 +7014,8 @@ msgid "Seat: seat number"
|
||||
msgstr "Ulica i numer domu"
|
||||
|
||||
#: pretix/base/pdf.py:487
|
||||
#, fuzzy
|
||||
#| msgid "Date and time"
|
||||
msgid "Date and time of first scan"
|
||||
msgstr "Data i czas"
|
||||
msgstr "Data i czas pierwszego skanu"
|
||||
|
||||
#: pretix/base/pdf.py:493
|
||||
#, fuzzy
|
||||
@@ -7761,6 +7728,8 @@ msgid ""
|
||||
"You selected a membership for the product \"{product}\" which does not "
|
||||
"require a membership."
|
||||
msgstr ""
|
||||
"Wybrano członkowstwo dla produktu \"{product}\", któryh nie wymaga "
|
||||
"członkowstwa."
|
||||
|
||||
#: pretix/base/services/memberships.py:113
|
||||
#, python-brace-format
|
||||
@@ -7768,13 +7737,12 @@ msgid ""
|
||||
"You selected the product \"{product}\" which requires an active membership "
|
||||
"to be selected."
|
||||
msgstr ""
|
||||
"Wybrano produkt \"{product}\" który wymaga wybrania aktywnego członkowstwa."
|
||||
|
||||
#: pretix/base/services/memberships.py:142
|
||||
#, fuzzy
|
||||
#| msgid "You cannot select an item that belongs to a different event."
|
||||
msgid ""
|
||||
"You selected a membership that is connected to a different customer account."
|
||||
msgstr "Wybrano przedmiot należący do innego wydarzenia."
|
||||
msgstr "Wybrano członkowstwo, które jest połączone z innym kontem klienta."
|
||||
|
||||
#: pretix/base/services/memberships.py:147
|
||||
#, fuzzy
|
||||
@@ -7792,6 +7760,8 @@ msgid ""
|
||||
"You selected a membership that is valid from {start} to {end}, but selected "
|
||||
"an event taking place at {date}."
|
||||
msgstr ""
|
||||
"Wybrano członkowstwo które jest ważne od {start} do {end}, ale wybrane "
|
||||
"wydarzenie ma miejsce {date}."
|
||||
|
||||
#: pretix/base/services/memberships.py:174
|
||||
#, python-brace-format
|
||||
@@ -7799,6 +7769,8 @@ msgid ""
|
||||
"You selected a membership of type \"{type}\", which is not allowed for the "
|
||||
"product \"{product}\"."
|
||||
msgstr ""
|
||||
"Wybrano członkowstwo typu \"{type}\", które nie jest dozwolone dla produktu "
|
||||
"\"{product}\"."
|
||||
|
||||
#: pretix/base/services/memberships.py:183
|
||||
#, python-brace-format
|
||||
@@ -29774,7 +29746,7 @@ msgstr ""
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:243
|
||||
msgid "If you already ordered a ticket"
|
||||
msgstr ""
|
||||
msgstr "Jeżeli bilet jest już zamówiony"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:247
|
||||
msgid ""
|
||||
@@ -29786,7 +29758,7 @@ msgstr ""
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:256
|
||||
msgid "Resend order link"
|
||||
msgstr ""
|
||||
msgstr "Wyślij ponownie link do zamówienia"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/order.html:12
|
||||
#: pretix/presale/templates/pretixpresale/event/order.html:29
|
||||
@@ -30228,10 +30200,8 @@ msgstr ""
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/resend_link.html:4
|
||||
#: pretix/presale/templates/pretixpresale/event/resend_link.html:11
|
||||
#, fuzzy
|
||||
#| msgid "Pending orders"
|
||||
msgid "Resend order links"
|
||||
msgstr "Zamówienia w toku"
|
||||
msgstr "Wyślij ponownie linki do zamówienia"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/resend_link.html:15
|
||||
msgid ""
|
||||
@@ -30964,6 +30934,8 @@ msgid ""
|
||||
"We've added you to the waiting list. We will send an email to {email} as "
|
||||
"soon as this product gets available again."
|
||||
msgstr ""
|
||||
"Dodaliśmy Cię do listy oczekujących. Wyślemi Ci mail na {email} kiedy "
|
||||
"produkt będzie znów dostępny."
|
||||
|
||||
#: pretix/presale/views/waiting.py:160
|
||||
#, fuzzy
|
||||
|
||||
@@ -25,9 +25,18 @@ from django.forms.models import ModelChoiceIterator
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from pretix.plugins.badges.models import BadgeItem, BadgeLayout
|
||||
from pretix.plugins.badges.templates import TEMPLATES
|
||||
|
||||
|
||||
class BadgeLayoutForm(forms.ModelForm):
|
||||
template = forms.ChoiceField(
|
||||
label=_('Template'),
|
||||
help_text=_('You can modify the layout or change to a different page size in the next step.'),
|
||||
choices=((k, v['label']) for k, v in TEMPLATES.items()),
|
||||
widget=forms.RadioSelect,
|
||||
initial='a6l',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = BadgeLayout
|
||||
fields = ('name',)
|
||||
|
||||
237
src/pretix/plugins/badges/templates.py
Normal file
237
src/pretix/plugins/badges/templates.py
Normal file
@@ -0,0 +1,237 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django.utils.text import format_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from reportlab.lib import pagesizes
|
||||
from reportlab.lib.units import mm
|
||||
|
||||
|
||||
def _simple_template(w, h):
|
||||
name_size = max(min(20, w / 20), 12) # Heuristic for font size
|
||||
company_size = name_size - 2
|
||||
return [
|
||||
{
|
||||
"type": "textarea",
|
||||
"page": 1,
|
||||
"locale": "",
|
||||
"left": "5.00",
|
||||
"bottom": "%.2f" % (((h - company_size * 1.5 - name_size) / 2 + company_size * 1.5) / mm),
|
||||
"fontsize": name_size,
|
||||
"lineheight": "1",
|
||||
"color": [0, 0, 0, 1],
|
||||
"fontfamily": "Open Sans",
|
||||
"bold": True,
|
||||
"italic": False,
|
||||
"width": "%.2f" % (w / mm - 10),
|
||||
"downward": False,
|
||||
"content": "attendee_name",
|
||||
"text": "John Doe",
|
||||
"text_i18n": {},
|
||||
"rotation": 0,
|
||||
"align": "center",
|
||||
},
|
||||
{
|
||||
"type": "textarea",
|
||||
"page": 1,
|
||||
"locale": "",
|
||||
"left": "5.00",
|
||||
"bottom": "%.2f" % ((((h - company_size * 1.5 - name_size) / 2) + company_size) / mm),
|
||||
"fontsize": company_size,
|
||||
"lineheight": "1",
|
||||
"color": [0, 0, 0, 1],
|
||||
"fontfamily": "Open Sans",
|
||||
"bold": False,
|
||||
"italic": False,
|
||||
"width": "%.2f" % (w / mm - 10),
|
||||
"downward": True,
|
||||
"content": "attendee_company",
|
||||
"text": "Sample company",
|
||||
"text_i18n": {},
|
||||
"rotation": 0,
|
||||
"align": "center",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
TEMPLATES = {
|
||||
"a6l": {
|
||||
"label": _("A6 landscape"),
|
||||
"pagesize": pagesizes.landscape(pagesizes.A6),
|
||||
"layout": _simple_template(*pagesizes.landscape(pagesizes.A6)),
|
||||
},
|
||||
"a6p": {
|
||||
"label": _("A6 portrait"),
|
||||
"pagesize": pagesizes.portrait(pagesizes.A6),
|
||||
"layout": _simple_template(*pagesizes.portrait(pagesizes.A6)),
|
||||
},
|
||||
"a7l": {
|
||||
"label": _("A7 landscape"),
|
||||
"pagesize": pagesizes.landscape(pagesizes.A7),
|
||||
"layout": _simple_template(*pagesizes.landscape(pagesizes.A7)),
|
||||
},
|
||||
"a7p": {
|
||||
"label": _("A7 portrait"),
|
||||
"pagesize": pagesizes.portrait(pagesizes.A7),
|
||||
"layout": _simple_template(*pagesizes.portrait(pagesizes.A7)),
|
||||
},
|
||||
"82x203butterfly": {
|
||||
"label": format_lazy(_("{width} x {height} mm butterfly badge"), width=82, height=203),
|
||||
"pagesize": (82 * mm, 203 * mm),
|
||||
"layout": [
|
||||
{
|
||||
"type": "textarea",
|
||||
"page": 1,
|
||||
"locale": "",
|
||||
"left": "5.00",
|
||||
"bottom": "152.55",
|
||||
"fontsize": "20.0",
|
||||
"lineheight": "1",
|
||||
"color": [0, 0, 0, 1],
|
||||
"fontfamily": "Open Sans",
|
||||
"bold": True,
|
||||
"italic": False,
|
||||
"width": "72.00",
|
||||
"downward": False,
|
||||
"content": "attendee_name",
|
||||
"text": "John Doe",
|
||||
"text_i18n": {},
|
||||
"rotation": 0,
|
||||
"align": "center",
|
||||
},
|
||||
{
|
||||
"type": "textarea",
|
||||
"page": 1,
|
||||
"locale": "",
|
||||
"left": "5.00",
|
||||
"bottom": "144.55",
|
||||
"fontsize": "18.0",
|
||||
"lineheight": "1",
|
||||
"color": [0, 0, 0, 1],
|
||||
"fontfamily": "Open Sans",
|
||||
"bold": False,
|
||||
"italic": False,
|
||||
"width": "72.00",
|
||||
"downward": False,
|
||||
"content": "attendee_company",
|
||||
"text": "Sample company",
|
||||
"text_i18n": {},
|
||||
"rotation": 0,
|
||||
"align": "center",
|
||||
},
|
||||
{
|
||||
"type": "textarea",
|
||||
"page": 1,
|
||||
"locale": "",
|
||||
"left": "77.10",
|
||||
"bottom": "34.68",
|
||||
"fontsize": "20.0",
|
||||
"lineheight": "1",
|
||||
"color": [0, 0, 0, 1],
|
||||
"fontfamily": "Open Sans",
|
||||
"bold": True,
|
||||
"italic": False,
|
||||
"width": "72.00",
|
||||
"downward": False,
|
||||
"content": "attendee_name",
|
||||
"text": "John Doe",
|
||||
"text_i18n": {},
|
||||
"rotation": 180,
|
||||
"align": "center",
|
||||
},
|
||||
{
|
||||
"type": "textarea",
|
||||
"page": 1,
|
||||
"locale": "",
|
||||
"left": "77.06",
|
||||
"bottom": "44.28",
|
||||
"fontsize": "18.0",
|
||||
"lineheight": "1",
|
||||
"color": [0, 0, 0, 1],
|
||||
"fontfamily": "Open Sans",
|
||||
"bold": False,
|
||||
"italic": False,
|
||||
"width": "72.00",
|
||||
"downward": False,
|
||||
"content": "attendee_company",
|
||||
"text": "Sample company",
|
||||
"text_i18n": {},
|
||||
"rotation": 180,
|
||||
"align": "center",
|
||||
},
|
||||
],
|
||||
},
|
||||
"100x50": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=100, height=50),
|
||||
"pagesize": (100 * mm, 50 * mm),
|
||||
"layout": _simple_template(100 * mm, 50 * mm),
|
||||
},
|
||||
"83x50": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=83, height=50),
|
||||
"pagesize": (83 * mm, 50 * mm),
|
||||
"layout": _simple_template(83 * mm, 50 * mm),
|
||||
},
|
||||
"80x50": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=80, height=50),
|
||||
"pagesize": (80 * mm, 50 * mm),
|
||||
"layout": _simple_template(80 * mm, 50 * mm),
|
||||
},
|
||||
"75x52": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=75, height=52),
|
||||
"pagesize": (75 * mm, 52 * mm),
|
||||
"layout": _simple_template(75 * mm, 52 * mm),
|
||||
},
|
||||
"70x36": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=70, height=36),
|
||||
"pagesize": (70 * mm, 36 * mm),
|
||||
"layout": _simple_template(70 * mm, 36 * mm),
|
||||
},
|
||||
"63x29": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=63, height=29),
|
||||
"pagesize": (63.5 * mm, 29.6 * mm),
|
||||
"layout": _simple_template(63.5 * mm, 29.6 * mm),
|
||||
},
|
||||
"60x90": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=60, height=90),
|
||||
"pagesize": (60 * mm, 90 * mm),
|
||||
"layout": _simple_template(60 * mm, 90 * mm),
|
||||
},
|
||||
"54x90": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=54, height=90),
|
||||
"pagesize": (54 * mm, 90 * mm),
|
||||
"layout": _simple_template(54 * mm, 90 * mm),
|
||||
},
|
||||
"50x80": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=50, height=80),
|
||||
"pagesize": (50 * mm, 80 * mm),
|
||||
"layout": _simple_template(50 * mm, 80 * mm),
|
||||
},
|
||||
"40x75": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=40, height=75),
|
||||
"pagesize": (40 * mm, 75 * mm),
|
||||
"layout": _simple_template(40 * mm, 75 * mm),
|
||||
},
|
||||
"40x40": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=40, height=40),
|
||||
"pagesize": (40 * mm, 40 * mm),
|
||||
"layout": _simple_template(40 * mm, 40 * mm),
|
||||
},
|
||||
}
|
||||
@@ -18,21 +18,12 @@
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form_errors form %}
|
||||
{% bootstrap_field form.name layout="control" %}
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">
|
||||
{% trans "Badge design" %}
|
||||
</label>
|
||||
<div class="col-md-9">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can modify the design after you saved this page.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% if form.template %}
|
||||
{% bootstrap_field form.template layout="control" %}
|
||||
{% endif %}
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
{% trans "Save & continue" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -21,11 +21,13 @@
|
||||
#
|
||||
import json
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
from io import BytesIO
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.staticfiles import finders
|
||||
from django.core.files import File
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db import transaction
|
||||
from django.http import Http404
|
||||
@@ -37,6 +39,7 @@ from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
from django.views.generic import CreateView, DetailView, ListView
|
||||
from pypdf import PdfWriter
|
||||
from reportlab.lib import pagesizes
|
||||
from reportlab.pdfgen import canvas
|
||||
|
||||
@@ -51,6 +54,7 @@ from pretix.plugins.badges.tasks import badges_create_pdf
|
||||
|
||||
from ...helpers.compat import CompatDeleteView
|
||||
from .models import BadgeLayout
|
||||
from .templates import TEMPLATES
|
||||
|
||||
|
||||
class LayoutListView(EventPermissionRequiredMixin, ListView):
|
||||
@@ -71,14 +75,32 @@ class LayoutCreate(EventPermissionRequiredMixin, CreateView):
|
||||
context_object_name = 'layout'
|
||||
success_url = '/ignored'
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
if self.copy_from:
|
||||
del form.fields['template']
|
||||
return form
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
form.instance.event = self.request.event
|
||||
if not self.request.event.badge_layouts.filter(default=True).exists():
|
||||
form.instance.default = True
|
||||
messages.success(self.request, _('The new badge layout has been created.'))
|
||||
if not self.copy_from:
|
||||
form.instance.layout = json.dumps(TEMPLATES[form.cleaned_data["template"]]["layout"])
|
||||
super().form_valid(form)
|
||||
if form.instance.background and form.instance.background.name:
|
||||
if not self.copy_from:
|
||||
p = PdfWriter()
|
||||
p.add_blank_page(
|
||||
width=Decimal('%.5f' % TEMPLATES[form.cleaned_data["template"]]["pagesize"][0]),
|
||||
height=Decimal('%.5f' % TEMPLATES[form.cleaned_data["template"]]["pagesize"][1]),
|
||||
)
|
||||
buffer = BytesIO()
|
||||
p.write(buffer)
|
||||
buffer.seek(0)
|
||||
form.instance.background.save('background.pdf', ContentFile(buffer.read()))
|
||||
elif form.instance.background and form.instance.background.name:
|
||||
form.instance.background.save('background.pdf', form.instance.background)
|
||||
form.instance.log_action('pretix.plugins.badges.layout.added', user=self.request.user,
|
||||
data=dict(form.cleaned_data))
|
||||
|
||||
@@ -57,6 +57,7 @@ var pretixpaypal = {
|
||||
wechatpay: gettext('WeChat Pay'),
|
||||
mercadopago: gettext('Mercado Pago')
|
||||
},
|
||||
readyToSubmitApproval: false,
|
||||
|
||||
load: function () {
|
||||
if (pretixpaypal.paypal === null) {
|
||||
@@ -141,6 +142,8 @@ var pretixpaypal = {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("visibilitychange", this.onApproveSubmit);
|
||||
},
|
||||
|
||||
ready: function () {
|
||||
@@ -225,12 +228,16 @@ var pretixpaypal = {
|
||||
|
||||
let method = pretixpaypal.paypage ? "wallet" : pretixpaypal.method.method;
|
||||
let selectorstub = "#payment_paypal_" + method;
|
||||
var $form = $(selectorstub + "_oid").closest("form");
|
||||
// Insert the tokens into the form so it gets submitted to the server
|
||||
// Insert the tokens into the form, so it gets submitted to the server
|
||||
$(selectorstub + "_oid").val(pretixpaypal.order_id);
|
||||
$(selectorstub + "_payer").val(pretixpaypal.payer_id);
|
||||
// and submit
|
||||
$form.get(0).submit();
|
||||
|
||||
// We are moving the submission to a separate function, which is also an EventListener, since
|
||||
// SFSafariView refuses to submit a form that is not visible. Unfortunately, that is exactly the case
|
||||
// when the ticket shop is used on iOS within an SFSafariView and the PayPal payment popup has not
|
||||
// closed itself quickly enough.
|
||||
pretixpaypal.readyToSubmitApproval = true;
|
||||
pretixpaypal.onApproveSubmit();
|
||||
|
||||
// billingToken: null
|
||||
// facilitatorAccessToken: "A21AAL_fEu0gDD-sIXyOy65a6MjgSJJrhmxuPcxxUGnL5gW2DzTxiiAksfoC4x8hD-BjeY1LsFVKl7ceuO7UR1a9pQr8Q_AVw"
|
||||
@@ -249,6 +256,16 @@ var pretixpaypal = {
|
||||
}
|
||||
},
|
||||
|
||||
onApproveSubmit: function() {
|
||||
if (document.visibilityState === "visible" && pretixpaypal.readyToSubmitApproval === true) {
|
||||
let method = pretixpaypal.paypage ? "wallet" : pretixpaypal.method.method;
|
||||
let selectorstub = "#payment_paypal_" + method;
|
||||
var $form = $(selectorstub + "_oid").closest("form");
|
||||
|
||||
$form.get(0).submit();
|
||||
}
|
||||
},
|
||||
|
||||
renderAPMs: function () {
|
||||
pretixpaypal.restore();
|
||||
let inputselector = $("input[name=payment][value=paypal_apm]");
|
||||
|
||||
@@ -570,7 +570,7 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
rate=a.tax_rate,
|
||||
)
|
||||
else:
|
||||
v.initial_price = v.display_price
|
||||
v.initial_price = v.suggested_price
|
||||
i.expand = any(v.initial for v in i.available_variations)
|
||||
else:
|
||||
i.initial = len(current_addon_products[i.pk, None])
|
||||
@@ -584,7 +584,7 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
rate=a.tax_rate,
|
||||
)
|
||||
else:
|
||||
i.initial_price = i.display_price
|
||||
i.initial_price = i.suggested_price
|
||||
|
||||
if items:
|
||||
formsetentry['categories'].append({
|
||||
@@ -1515,6 +1515,9 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
str(m) for m in self.confirm_messages.values()
|
||||
]
|
||||
}
|
||||
unlock_hashes = request.session.get('pretix_unlock_hashes', [])
|
||||
if unlock_hashes:
|
||||
meta_info['unlock_hashes'] = unlock_hashes
|
||||
for receiver, response in order_meta_from_request.send(sender=request.event, request=request):
|
||||
meta_info.update(response)
|
||||
|
||||
|
||||
@@ -139,7 +139,15 @@
|
||||
placeholder="0"
|
||||
min="{% if event.settings.display_net_prices %}{{ var.display_price.net|money_numberfield:event.currency }}{% else %}{{ var.display_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
name="cp_{{ form.pos.pk }}_variation_{{ item.id }}_{{ var.id }}_price"
|
||||
title="{% blocktrans trimmed with item=var.value %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
{% if var.initial_price.gross != var.display_price.gross %}
|
||||
{% if event.settings.display_net_prices %}
|
||||
title="{% blocktrans trimmed with item=var.value price=var.display_price.net|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=var.value price=var.display_price.gross|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=var.value %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
step="any"
|
||||
value="{% if event.settings.display_net_prices %}{{ var.initial_price.net|money_numberfield:event.currency }}{% else %}{{ var.initial_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
>
|
||||
@@ -268,7 +276,15 @@
|
||||
id="price-item-{{ form.pos.pk }}-{{ item.pk }}"
|
||||
min="{% if event.settings.display_net_prices %}{{ item.display_price.net|money_numberfield:event.currency }}{% else %}{{ item.display_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
name="cp_{{ form.pos.pk }}_item_{{ item.id }}_price"
|
||||
title="{% blocktrans trimmed with item=item.name %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
{% if item.initial_price.gross != item.display_price.gross %}
|
||||
{% if event.settings.display_net_prices %}
|
||||
title="{% blocktrans trimmed with item=item.name price=item.display_price.net|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=item.name price=item.display_price.gross|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=item.name %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
value="{% if event.settings.display_net_prices %}{{ item.initial_price.net|money_numberfield:event.currency }}{% else %}{{ item.initial_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
step="any">
|
||||
</div>
|
||||
|
||||
@@ -142,10 +142,6 @@
|
||||
<td>
|
||||
{% if new_pending_sum > 0 %}
|
||||
<strong>{% trans "You will need to pay" %}</strong>
|
||||
<br>
|
||||
<span class="text-muted">
|
||||
{% trans "Your entire order will be considered unpaid until you paid this difference." %}
|
||||
</span>
|
||||
{% else %}
|
||||
<strong>{% trans "You will be refunded" %}</strong>
|
||||
<br>
|
||||
@@ -184,6 +180,12 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% if new_pending_sum > 0 and order.status == "p" %}
|
||||
<div class="alert alert-warning">
|
||||
<strong>{% trans "Your entire order will be considered unpaid until you paid this difference." %}</strong>
|
||||
{% trans "You might not be able to use any of the tickets in your order until this payment has been received." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for k, l in request.POST.lists %}
|
||||
{% for v in l %}
|
||||
<input type="hidden" name="{{ k }}" value="{{ v }}">
|
||||
|
||||
@@ -138,9 +138,17 @@
|
||||
placeholder="0"
|
||||
min="{% if event.settings.display_net_prices %}{{ var.display_price.net|money_numberfield:event.currency }}{% else %}{{ var.display_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
name="price_{{ item.id }}_{{ var.id }}"
|
||||
title="{% blocktrans trimmed with item=var.value %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
{% if var.suggested_price.gross != var.display_price.gross %}
|
||||
{% if event.settings.display_net_prices %}
|
||||
title="{% blocktrans trimmed with item=var.value price=var.display_price.net|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=var.value price=var.display_price.gross|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=var.value %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
step="any"
|
||||
value="{% if event.settings.display_net_prices %}{{ var.display_price.net|money_numberfield:event.currency }}{% else %}{{ var.display_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
value="{% if event.settings.display_net_prices %}{{ var.suggested_price.net|money_numberfield:event.currency }}{% else %}{{ var.suggested_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
>
|
||||
</div>
|
||||
<p>
|
||||
@@ -284,8 +292,16 @@
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
min="{% if event.settings.display_net_prices %}{{ item.display_price.net|money_numberfield:event.currency }}{% else %}{{ item.display_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
name="price_{{ item.id }}"
|
||||
title="{% blocktrans trimmed with item=item.name %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
value="{% if event.settings.display_net_prices %}{{ item.display_price.net|money_numberfield:event.currency }}{% else %}{{ item.display_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
{% if item.suggested_price.gross != item.display_price.gross %}
|
||||
{% if event.settings.display_net_prices %}
|
||||
title="{% blocktrans trimmed with item=item.name price=item.display_price.net|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=item.name price=item.display_price.gross|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=item.name %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
value="{% if event.settings.display_net_prices %}{{ item.suggested_price.net|money_numberfield:event.currency }}{% else %}{{ item.suggested_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
step="any">
|
||||
</div>
|
||||
<p>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="subevent" value="{{ subevent.id|default_if_none:"" }}" />
|
||||
<input type="hidden" name="next" value="{{ request.path }}" />
|
||||
<input type="hidden" name="next" value="{% if next_url %}{{ next_url }}{% else %}{{ request.path }}{% endif %}" />
|
||||
<div class="col-md-4 col-sm-6 col-xs-12">
|
||||
<button class="btn btn-block btn-primary" type="submit">
|
||||
{% trans "Redeem voucher" %}
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
Choose payment method: {{ code }}
|
||||
{% endblocktrans %}
|
||||
</h2>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Please note: If you change your payment method, your order total will change by the
|
||||
amount displayed to the right of each method.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% if show_fees %}
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Please note: If you change your payment method, your order total will change by the
|
||||
amount displayed to the right of each method.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="panel-group" id="payment_accordion">
|
||||
|
||||
@@ -166,8 +166,16 @@
|
||||
placeholder="0"
|
||||
min="{% if event.settings.display_net_prices %}{{ var.display_price.net|stringformat:"0.2f" }}{% else %}{{ var.display_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
name="price_{{ item.id }}_{{ var.id }}"
|
||||
title="{% blocktrans trimmed with item=var.value %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
value="{% if event.settings.display_net_prices %}{{var.display_price.net|stringformat:"0.2f" }}{% else %}{{ var.display_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
{% if var.suggested_price.gross != var.display_price.gross %}
|
||||
{% if event.settings.display_net_prices %}
|
||||
title="{% blocktrans trimmed with item=var.value price=var.display_price.net|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=var.value price=var.display_price.gross|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=var.value %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
value="{% if event.settings.display_net_prices %}{{var.suggested_price.net|stringformat:"0.2f" }}{% else %}{{ var.suggested_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
step="any">
|
||||
</div>
|
||||
<p>
|
||||
@@ -309,8 +317,16 @@
|
||||
<input type="number" class="form-control input-item-price" placeholder="0"
|
||||
min="{% if event.settings.display_net_prices %}{{ item.display_price.net|stringformat:"0.2f" }}{% else %}{{ item.display_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
name="price_{{ item.id }}"
|
||||
title="{% blocktrans trimmed with item=item.name %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
value="{% if event.settings.display_net_prices %}{{ item.display_price.net|stringformat:"0.2f" }}{% else %}{{ item.display_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
{% if item.suggested_price.gross != item.display_price.gross %}
|
||||
{% if event.settings.display_net_prices %}
|
||||
title="{% blocktrans trimmed with item=item.name price=item.display_price.net|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=item.name price=item.display_price.gross|money:event.currency %}Modify price for {{ item }}, at least {{ price }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
title="{% blocktrans trimmed with item=item.name %}Modify price for {{ item }}{% endblocktrans %}"
|
||||
{% endif %}
|
||||
value="{% if event.settings.display_net_prices %}{{ item.suggested_price.net|stringformat:"0.2f" }}{% else %}{{ item.suggested_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
step="any">
|
||||
</div>
|
||||
<p>
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
{% block title %}{% trans "Voucher redemption" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if show_cart %}
|
||||
{% include "pretixpresale/event/fragment_cart_box.html" with open=request.GET.show_cart %}
|
||||
{% endif %}
|
||||
|
||||
<h2>{% trans "Redeem a voucher" %}</h2>
|
||||
{% include "pretixpresale/event/fragment_voucher_form.html" %}
|
||||
{% include "pretixpresale/event/fragment_voucher_form.html" with next_url=request.path|add:"?show_cart=true" %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -600,8 +600,11 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, CartMixin, TemplateView
|
||||
context['cart_redirect'] = eventreverse(self.request.event, 'presale:event.checkout.start',
|
||||
kwargs={'cart_namespace': kwargs.get('cart_namespace') or ''})
|
||||
else:
|
||||
context['cart_redirect'] = eventreverse(self.request.event, 'presale:event.index',
|
||||
kwargs={'cart_namespace': kwargs.get('cart_namespace') or ''})
|
||||
if 'next' in self.request.GET and url_has_allowed_host_and_scheme(self.request.GET.get("next"), allowed_hosts=None):
|
||||
context['cart_redirect'] = self.request.GET.get('next')
|
||||
else:
|
||||
context['cart_redirect'] = eventreverse(self.request.event, 'presale:event.index',
|
||||
kwargs={'cart_namespace': kwargs.get('cart_namespace') or ''})
|
||||
if context['cart_redirect'].startswith('https:'):
|
||||
context['cart_redirect'] = '/' + context['cart_redirect'].split('/', 3)[3]
|
||||
return context
|
||||
@@ -634,7 +637,10 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, CartMixin, TemplateView
|
||||
else:
|
||||
err = error_messages['voucher_invalid']
|
||||
else:
|
||||
return render(request, 'pretixpresale/event/voucher_form.html')
|
||||
context = {}
|
||||
context['cart'] = self.get_cart()
|
||||
context['show_cart'] = context['cart']['positions']
|
||||
return render(request, 'pretixpresale/event/voucher_form.html', context)
|
||||
|
||||
if request.event.presale_start and now() < request.event.presale_start:
|
||||
err = error_messages['not_started']
|
||||
|
||||
@@ -127,52 +127,77 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web', require
|
||||
else:
|
||||
prefetch_membership_types = []
|
||||
|
||||
prefetch_var = Prefetch(
|
||||
'variations',
|
||||
to_attr='available_variations',
|
||||
queryset=ItemVariation.objects.using(settings.DATABASE_REPLICA).annotate(
|
||||
subevent_disabled=Exists(
|
||||
SubEventItemVariation.objects.filter(
|
||||
Q(disabled=True) | Q(available_from__gt=now()) | Q(available_until__lt=now()),
|
||||
variation_id=OuterRef('pk'),
|
||||
subevent=subevent,
|
||||
)
|
||||
),
|
||||
).filter(
|
||||
variation_q,
|
||||
active=True,
|
||||
sales_channels__contains=channel,
|
||||
quotas__isnull=False,
|
||||
subevent_disabled=False
|
||||
).prefetch_related(
|
||||
*prefetch_membership_types,
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(
|
||||
subevent=subevent))
|
||||
).distinct()
|
||||
)
|
||||
prefetch_quotas = Prefetch(
|
||||
'quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)
|
||||
)
|
||||
prefetch_bundles = Prefetch(
|
||||
'bundles',
|
||||
queryset=ItemBundle.objects.using(settings.DATABASE_REPLICA).prefetch_related(
|
||||
Prefetch('bundled_item',
|
||||
queryset=event.items.using(settings.DATABASE_REPLICA).select_related(
|
||||
'tax_rule').prefetch_related(
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(
|
||||
subevent=subevent)),
|
||||
)),
|
||||
Prefetch('bundled_variation',
|
||||
queryset=ItemVariation.objects.using(
|
||||
settings.DATABASE_REPLICA
|
||||
).select_related('item', 'item__tax_rule').filter(item__event=event).prefetch_related(
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(
|
||||
subevent=subevent)),
|
||||
)),
|
||||
)
|
||||
)
|
||||
|
||||
items = base_qs.using(settings.DATABASE_REPLICA).filter_available(channel=channel, voucher=voucher, allow_addons=allow_addons).select_related(
|
||||
'category', 'tax_rule', # for re-grouping
|
||||
'hidden_if_available',
|
||||
).prefetch_related(
|
||||
*prefetch_membership_types,
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
|
||||
Prefetch('bundles',
|
||||
queryset=ItemBundle.objects.using(settings.DATABASE_REPLICA).prefetch_related(
|
||||
Prefetch('bundled_item',
|
||||
queryset=event.items.using(settings.DATABASE_REPLICA).select_related('tax_rule').prefetch_related(
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
|
||||
)),
|
||||
Prefetch('bundled_variation',
|
||||
queryset=ItemVariation.objects.using(
|
||||
settings.DATABASE_REPLICA
|
||||
).select_related('item', 'item__tax_rule').filter(item__event=event).prefetch_related(
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
|
||||
)),
|
||||
)),
|
||||
Prefetch('variations', to_attr='available_variations',
|
||||
queryset=ItemVariation.objects.using(settings.DATABASE_REPLICA).annotate(
|
||||
subevent_disabled=Exists(
|
||||
SubEventItemVariation.objects.filter(
|
||||
Q(disabled=True) | Q(available_from__gt=now()) | Q(available_until__lt=now()),
|
||||
variation_id=OuterRef('pk'),
|
||||
subevent=subevent,
|
||||
)
|
||||
),
|
||||
).filter(
|
||||
variation_q,
|
||||
active=True,
|
||||
sales_channels__contains=channel,
|
||||
quotas__isnull=False,
|
||||
subevent_disabled=False
|
||||
).prefetch_related(
|
||||
*prefetch_membership_types,
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent))
|
||||
).distinct()),
|
||||
Prefetch(
|
||||
'hidden_if_item_available',
|
||||
queryset=event.items.annotate(
|
||||
has_variations=Count('variations'),
|
||||
).prefetch_related(
|
||||
prefetch_var,
|
||||
prefetch_quotas,
|
||||
prefetch_bundles,
|
||||
)
|
||||
),
|
||||
prefetch_quotas,
|
||||
prefetch_var,
|
||||
prefetch_bundles,
|
||||
).annotate(
|
||||
quotac=Count('quotas'),
|
||||
has_variations=Count('variations'),
|
||||
@@ -256,6 +281,19 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web', require
|
||||
item._remove = True
|
||||
continue
|
||||
|
||||
if item.hidden_if_item_available:
|
||||
if item.hidden_if_item_available.has_variations:
|
||||
dependency_available = any(
|
||||
var.check_quotas(subevent=subevent, _cache=quota_cache, include_bundled=True)[0] == Quota.AVAILABILITY_OK
|
||||
for var in item.hidden_if_item_available.available_variations
|
||||
)
|
||||
else:
|
||||
q = item.hidden_if_item_available.check_quotas(subevent=subevent, _cache=quota_cache, include_bundled=True)
|
||||
dependency_available = q[0] == Quota.AVAILABILITY_OK
|
||||
if dependency_available:
|
||||
item._remove = True
|
||||
continue
|
||||
|
||||
if item.require_membership and item.require_membership_hidden:
|
||||
if not memberships or not any([m.membership_type in item.require_membership_types.all() for m in memberships]):
|
||||
item._remove = True
|
||||
@@ -300,6 +338,10 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web', require
|
||||
price = original_price
|
||||
|
||||
item.display_price = item.tax(price, currency=event.currency, include_bundled=True)
|
||||
if item.free_price and item.free_price_suggestion is not None:
|
||||
item.suggested_price = item.tax(max(price, item.free_price_suggestion), currency=event.currency, include_bundled=True)
|
||||
else:
|
||||
item.suggested_price = item.display_price
|
||||
|
||||
if price != original_price:
|
||||
item.original_price = item.tax(original_price, currency=event.currency, include_bundled=True)
|
||||
@@ -346,6 +388,15 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web', require
|
||||
|
||||
var.display_price = var.tax(price, currency=event.currency, include_bundled=True)
|
||||
|
||||
if item.free_price and var.free_price_suggestion is not None:
|
||||
var.suggested_price = item.tax(max(price, var.free_price_suggestion), currency=event.currency,
|
||||
include_bundled=True)
|
||||
elif item.free_price and item.free_price_suggestion is not None:
|
||||
var.suggested_price = item.tax(max(price, item.free_price_suggestion), currency=event.currency,
|
||||
include_bundled=True)
|
||||
else:
|
||||
var.suggested_price = var.display_price
|
||||
|
||||
if price != original_price:
|
||||
var.original_price = var.tax(original_price, currency=event.currency, include_bundled=True)
|
||||
else:
|
||||
|
||||
@@ -289,9 +289,17 @@ class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TicketPageMixin,
|
||||
ctx['can_pay'] = False
|
||||
|
||||
for provider in self.request.event.get_payment_providers().values():
|
||||
if provider.is_enabled and provider.order_change_allowed(self.order):
|
||||
ctx['can_pay'] = True
|
||||
break
|
||||
if provider.is_enabled:
|
||||
|
||||
if 'request' in inspect.signature(provider.order_change_allowed).parameters:
|
||||
if provider.is_enabled and provider.order_change_allowed(self.order, request=self.request):
|
||||
ctx['can_pay'] = True
|
||||
break
|
||||
|
||||
else:
|
||||
if provider.is_enabled and provider.order_change_allowed(self.order):
|
||||
ctx['can_pay'] = True
|
||||
break
|
||||
|
||||
if lp and lp.state not in (OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_REFUNDED,
|
||||
OrderPayment.PAYMENT_STATE_CANCELED):
|
||||
@@ -665,8 +673,16 @@ class OrderPayChangeMethod(EventViewMixin, OrderDetailMixin, TemplateView):
|
||||
providers = []
|
||||
pending_sum = self.order.pending_sum
|
||||
for provider in self.request.event.get_payment_providers().values():
|
||||
if not provider.is_enabled or not provider.order_change_allowed(self.order):
|
||||
if not provider.is_enabled:
|
||||
continue
|
||||
|
||||
if 'request' in inspect.signature(provider.order_change_allowed).parameters:
|
||||
if not provider.order_change_allowed(self.order, request=self.request):
|
||||
continue
|
||||
else:
|
||||
if not provider.order_change_allowed(self.order):
|
||||
continue
|
||||
|
||||
current_fee = sum(f.value for f in self.open_fees) or Decimal('0.00')
|
||||
fee = provider.calculate_fee(pending_sum - current_fee)
|
||||
if 'order' in inspect.signature(provider.payment_form_render).parameters:
|
||||
@@ -1172,9 +1188,9 @@ class OrderPositionGiftCardDetails(EventViewMixin, OrderPositionDetailMixin, Lis
|
||||
|
||||
@cached_property
|
||||
def giftcard(self):
|
||||
return GiftCard.objects.filter(
|
||||
return get_object_or_404(GiftCard.objects.filter(
|
||||
Q(owner_ticket_id=self.position.pk) | Q(owner_ticket__addon_to_id=self.position.pk)
|
||||
).get(pk=self.kwargs['pk'])
|
||||
), pk=self.kwargs['pk'])
|
||||
|
||||
def get_queryset(self):
|
||||
return self.giftcard.transactions.order_by('-datetime', '-pk')
|
||||
@@ -1355,7 +1371,7 @@ class OrderChangeMixin:
|
||||
rate=a.tax_rate,
|
||||
)
|
||||
else:
|
||||
v.initial_price = v.display_price
|
||||
v.initial_price = v.suggested_price
|
||||
i.expand = any(v.initial for v in i.available_variations)
|
||||
else:
|
||||
i.initial = len(current_addon_products[i.pk, None])
|
||||
@@ -1369,7 +1385,7 @@ class OrderChangeMixin:
|
||||
rate=a.tax_rate,
|
||||
)
|
||||
else:
|
||||
i.initial_price = i.display_price
|
||||
i.initial_price = i.suggested_price
|
||||
|
||||
if items:
|
||||
p.addon_form['categories'].append({
|
||||
@@ -1579,6 +1595,8 @@ class OrderChangeMixin:
|
||||
raise OrderError(_('You may only change your order in a way that increases the total price.'))
|
||||
if ocm._totaldiff != Decimal('0.00') and pr == 'eq':
|
||||
raise OrderError(_('You may not change your order in a way that changes the total price.'))
|
||||
if ocm._totaldiff < Decimal('0.00') and self.order.total + ocm._totaldiff < self.order.payment_refund_sum and pr == 'gte_paid':
|
||||
raise OrderError(_('You may not change your order in a way that would require a refund.'))
|
||||
|
||||
if ocm._totaldiff > Decimal('0.00') and self.order.status == Order.STATUS_PAID:
|
||||
self.order.set_expires(
|
||||
|
||||
@@ -274,6 +274,7 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
'order_min': item.min_per_order,
|
||||
'order_max': item.order_max if not item.has_variations else None,
|
||||
'price': price_dict(item, item.display_price) if not item.has_variations else None,
|
||||
'suggested_price': price_dict(item, item.suggested_price) if not item.has_variations else None,
|
||||
'min_price': item.min_price if item.has_variations else None,
|
||||
'max_price': item.max_price if item.has_variations else None,
|
||||
'allow_waitinglist': item.allow_waitinglist,
|
||||
@@ -296,6 +297,7 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
'order_max': var.order_max,
|
||||
'description': str(rich_text(var.description, safelinks=False)) if var.description else None,
|
||||
'price': price_dict(item, var.display_price),
|
||||
'suggested_price': price_dict(item, var.suggested_price),
|
||||
'original_price': (
|
||||
(
|
||||
var.original_price.net
|
||||
|
||||
152
src/pretix/static/npm_dir/package-lock.json
generated
152
src/pretix/static/npm_dir/package-lock.json
generated
@@ -8,14 +8,14 @@
|
||||
"name": "pretix",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.23.0",
|
||||
"@babel/core": "^7.23.2",
|
||||
"@babel/preset-env": "^7.23.2",
|
||||
"@rollup/plugin-babel": "^6.0.3",
|
||||
"@rollup/plugin-node-resolve": "^15.2.1",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"rollup": "^2.79.1",
|
||||
"rollup-plugin-vue": "^5.0.1",
|
||||
"vue": "^2.7.14",
|
||||
"vue-template-compiler": "^2.7.14"
|
||||
"vue": "^2.7.15",
|
||||
"vue-template-compiler": "^2.7.15"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
@@ -51,19 +51,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core": {
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz",
|
||||
"integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz",
|
||||
"integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.0",
|
||||
"@babel/helper-compilation-targets": "^7.22.15",
|
||||
"@babel/helper-module-transforms": "^7.23.0",
|
||||
"@babel/helpers": "^7.23.0",
|
||||
"@babel/helpers": "^7.23.2",
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/traverse": "^7.23.0",
|
||||
"@babel/traverse": "^7.23.2",
|
||||
"@babel/types": "^7.23.0",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
@@ -445,12 +445,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz",
|
||||
"integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz",
|
||||
"integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/traverse": "^7.23.0",
|
||||
"@babel/traverse": "^7.23.2",
|
||||
"@babel/types": "^7.23.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1695,9 +1695,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-babel": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.3.tgz",
|
||||
"integrity": "sha512-fKImZKppa1A/gX73eg4JGo+8kQr/q1HBQaCGKECZ0v4YBBv3lFqi14+7xyApECzvkLTHCifx+7ntcrvtBIRcpg==",
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz",
|
||||
"integrity": "sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.18.6",
|
||||
"@rollup/pluginutils": "^5.0.1"
|
||||
@@ -1708,7 +1708,7 @@
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@types/babel__core": "^7.1.9",
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0"
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/babel__core": {
|
||||
@@ -1720,9 +1720,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-node-resolve": {
|
||||
"version": "15.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.1.tgz",
|
||||
"integrity": "sha512-nsbUg588+GDSu8/NS8T4UAshO6xeaOfINNuXeVHcKV02LJtoRaM1SiOacClw4kws1SFiNhdLGxlbMY9ga/zs/w==",
|
||||
"version": "15.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
|
||||
"integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
"@types/resolve": "1.20.2",
|
||||
@@ -1735,7 +1735,7 @@
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^2.78.0||^3.0.0"
|
||||
"rollup": "^2.78.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
@@ -1795,9 +1795,9 @@
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc": {
|
||||
"version": "2.7.14",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz",
|
||||
"integrity": "sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==",
|
||||
"version": "2.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.15.tgz",
|
||||
"integrity": "sha512-FCvIEevPmgCgqFBH7wD+3B97y7u7oj/Wr69zADBf403Tui377bThTjBvekaZvlRr4IwUAu3M6hYZeULZFJbdYg==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.18.4",
|
||||
"postcss": "^8.4.14",
|
||||
@@ -1805,9 +1805,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc/node_modules/postcss": {
|
||||
"version": "8.4.19",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
|
||||
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -1816,10 +1816,14 @@
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.4",
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
@@ -3082,9 +3086,15 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
||||
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
@@ -4018,11 +4028,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "2.7.14",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.14.tgz",
|
||||
"integrity": "sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==",
|
||||
"version": "2.7.15",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.15.tgz",
|
||||
"integrity": "sha512-a29fsXd2G0KMRqIFTpRgpSbWaNBK3lpCTOLuGLEDnlHWdjB8fwl6zyYZ8xCrqkJdatwZb4mGHiEfJjnw0Q6AwQ==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-sfc": "2.7.14",
|
||||
"@vue/compiler-sfc": "2.7.15",
|
||||
"csstype": "^3.1.0"
|
||||
}
|
||||
},
|
||||
@@ -4032,9 +4042,9 @@
|
||||
"integrity": "sha512-pZfGp+PW/IXEOyETE09xQHR1CKkR9HfHZdnMD/FVLUNI+HxYTa82evx5WrF6Kz4s82qtqHvMZ8MZpbk2zT2E1Q=="
|
||||
},
|
||||
"node_modules/vue-template-compiler": {
|
||||
"version": "2.7.14",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
|
||||
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
|
||||
"version": "2.7.15",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.15.tgz",
|
||||
"integrity": "sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==",
|
||||
"dependencies": {
|
||||
"de-indent": "^1.0.2",
|
||||
"he": "^1.2.0"
|
||||
@@ -4134,19 +4144,19 @@
|
||||
"integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ=="
|
||||
},
|
||||
"@babel/core": {
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz",
|
||||
"integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz",
|
||||
"integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==",
|
||||
"requires": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.0",
|
||||
"@babel/helper-compilation-targets": "^7.22.15",
|
||||
"@babel/helper-module-transforms": "^7.23.0",
|
||||
"@babel/helpers": "^7.23.0",
|
||||
"@babel/helpers": "^7.23.2",
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/traverse": "^7.23.0",
|
||||
"@babel/traverse": "^7.23.2",
|
||||
"@babel/types": "^7.23.0",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
@@ -4423,12 +4433,12 @@
|
||||
}
|
||||
},
|
||||
"@babel/helpers": {
|
||||
"version": "7.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz",
|
||||
"integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz",
|
||||
"integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==",
|
||||
"requires": {
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/traverse": "^7.23.0",
|
||||
"@babel/traverse": "^7.23.2",
|
||||
"@babel/types": "^7.23.0"
|
||||
}
|
||||
},
|
||||
@@ -5254,18 +5264,18 @@
|
||||
}
|
||||
},
|
||||
"@rollup/plugin-babel": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.3.tgz",
|
||||
"integrity": "sha512-fKImZKppa1A/gX73eg4JGo+8kQr/q1HBQaCGKECZ0v4YBBv3lFqi14+7xyApECzvkLTHCifx+7ntcrvtBIRcpg==",
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz",
|
||||
"integrity": "sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.18.6",
|
||||
"@rollup/pluginutils": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"@rollup/plugin-node-resolve": {
|
||||
"version": "15.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.1.tgz",
|
||||
"integrity": "sha512-nsbUg588+GDSu8/NS8T4UAshO6xeaOfINNuXeVHcKV02LJtoRaM1SiOacClw4kws1SFiNhdLGxlbMY9ga/zs/w==",
|
||||
"version": "15.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
|
||||
"integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==",
|
||||
"requires": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
"@types/resolve": "1.20.2",
|
||||
@@ -5318,9 +5328,9 @@
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
||||
},
|
||||
"@vue/compiler-sfc": {
|
||||
"version": "2.7.14",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz",
|
||||
"integrity": "sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==",
|
||||
"version": "2.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.15.tgz",
|
||||
"integrity": "sha512-FCvIEevPmgCgqFBH7wD+3B97y7u7oj/Wr69zADBf403Tui377bThTjBvekaZvlRr4IwUAu3M6hYZeULZFJbdYg==",
|
||||
"requires": {
|
||||
"@babel/parser": "^7.18.4",
|
||||
"postcss": "^8.4.14",
|
||||
@@ -5328,11 +5338,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"postcss": {
|
||||
"version": "8.4.19",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
|
||||
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||
"requires": {
|
||||
"nanoid": "^3.3.4",
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
@@ -6300,9 +6310,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
||||
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA=="
|
||||
},
|
||||
"native-request": {
|
||||
"version": "1.0.8",
|
||||
@@ -7038,11 +7048,11 @@
|
||||
"optional": true
|
||||
},
|
||||
"vue": {
|
||||
"version": "2.7.14",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.14.tgz",
|
||||
"integrity": "sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==",
|
||||
"version": "2.7.15",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.15.tgz",
|
||||
"integrity": "sha512-a29fsXd2G0KMRqIFTpRgpSbWaNBK3lpCTOLuGLEDnlHWdjB8fwl6zyYZ8xCrqkJdatwZb4mGHiEfJjnw0Q6AwQ==",
|
||||
"requires": {
|
||||
"@vue/compiler-sfc": "2.7.14",
|
||||
"@vue/compiler-sfc": "2.7.15",
|
||||
"csstype": "^3.1.0"
|
||||
}
|
||||
},
|
||||
@@ -7052,9 +7062,9 @@
|
||||
"integrity": "sha512-pZfGp+PW/IXEOyETE09xQHR1CKkR9HfHZdnMD/FVLUNI+HxYTa82evx5WrF6Kz4s82qtqHvMZ8MZpbk2zT2E1Q=="
|
||||
},
|
||||
"vue-template-compiler": {
|
||||
"version": "2.7.14",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
|
||||
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
|
||||
"version": "2.7.15",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.15.tgz",
|
||||
"integrity": "sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==",
|
||||
"requires": {
|
||||
"de-indent": "^1.0.2",
|
||||
"he": "^1.2.0"
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"private": true,
|
||||
"scripts": {},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.23.0",
|
||||
"@babel/core": "^7.23.2",
|
||||
"@babel/preset-env": "^7.23.2",
|
||||
"@rollup/plugin-babel": "^6.0.3",
|
||||
"@rollup/plugin-node-resolve": "^15.2.1",
|
||||
"vue": "^2.7.14",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"vue": "^2.7.15",
|
||||
"rollup": "^2.79.1",
|
||||
"rollup-plugin-vue": "^5.0.1",
|
||||
"vue-template-compiler": "^2.7.14"
|
||||
"vue-template-compiler": "^2.7.15"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ Vue.component('pricebox', {
|
||||
+ '<div v-if="free_price">'
|
||||
+ '{{ $root.currency }} '
|
||||
+ '<input type="number" class="pretix-widget-pricebox-price-input" placeholder="0" '
|
||||
+ ' :min="display_price_nonlocalized" :value="display_price_nonlocalized" :name="field_name"'
|
||||
+ ' :min="display_price_nonlocalized" :value="suggested_price_nonlocalized" :name="field_name"'
|
||||
+ ' step="any" aria-label="'+strings.price+'">'
|
||||
+ '</div>'
|
||||
+ '<small class="pretix-widget-pricebox-tax" v-if="price.rate != \'0.00\' && price.gross != \'0.00\'">'
|
||||
@@ -347,6 +347,7 @@ Vue.component('pricebox', {
|
||||
price: Object,
|
||||
free_price: Boolean,
|
||||
field_name: String,
|
||||
suggested_price: Object,
|
||||
original_price: String,
|
||||
mandatory_priced_addons: Boolean,
|
||||
},
|
||||
@@ -365,6 +366,17 @@ Vue.component('pricebox', {
|
||||
return parseFloat(this.price.gross).toFixed(2);
|
||||
}
|
||||
},
|
||||
suggested_price_nonlocalized: function () {
|
||||
var price = this.suggested_price;
|
||||
if (price === null) {
|
||||
price = this.price;
|
||||
}
|
||||
if (this.$root.display_net_prices) {
|
||||
return parseFloat(price.net).toFixed(2);
|
||||
} else {
|
||||
return parseFloat(price.gross).toFixed(2);
|
||||
}
|
||||
},
|
||||
original_line: function () {
|
||||
return this.$root.currency + " " + floatformat(parseFloat(this.original_price), 2);
|
||||
},
|
||||
@@ -420,7 +432,7 @@ Vue.component('variation', {
|
||||
// Price
|
||||
+ '<div class="pretix-widget-item-price-col">'
|
||||
+ '<pricebox :price="variation.price" :free_price="item.free_price" :original_price="orig_price" '
|
||||
+ ' :mandatory_priced_addons="item.mandatory_priced_addons"'
|
||||
+ ' :mandatory_priced_addons="item.mandatory_priced_addons" :suggested_price="variation.suggested_price"'
|
||||
+ ' :field_name="\'price_\' + item.id + \'_\' + variation.id" v-if="$root.showPrices">'
|
||||
+ '</pricebox>'
|
||||
+ '<span v-if="!$root.showPrices"> </span>'
|
||||
@@ -478,7 +490,7 @@ Vue.component('item', {
|
||||
// Price
|
||||
+ '<div class="pretix-widget-item-price-col">'
|
||||
+ '<pricebox :price="item.price" :free_price="item.free_price" v-if="!item.has_variations && $root.showPrices"'
|
||||
+ ' :mandatory_priced_addons="item.mandatory_priced_addons"'
|
||||
+ ' :mandatory_priced_addons="item.mandatory_priced_addons" :suggested_price="item.suggested_price"'
|
||||
+ ' :field_name="\'price_\' + item.id" :original_price="item.original_price">'
|
||||
+ '</pricebox>'
|
||||
+ '<div class="pretix-widget-pricebox" v-if="item.has_variations && $root.showPrices">{{ pricerange }}</div>'
|
||||
|
||||
@@ -23,6 +23,7 @@ filterwarnings =
|
||||
ignore:The 'warn' method is deprecated:DeprecationWarning
|
||||
ignore::django.utils.deprecation.RemovedInDjango51Warning:django.core.files.storage
|
||||
ignore:.*index_together.*:django.utils.deprecation.RemovedInDjango51Warning:
|
||||
ignore:.*get_storage_class.*:django.utils.deprecation.RemovedInDjango51Warning:compressor
|
||||
ignore::DeprecationWarning:mt940
|
||||
ignore::DeprecationWarning:cbor2
|
||||
ignore::DeprecationWarning:markdown
|
||||
|
||||
@@ -34,10 +34,13 @@
|
||||
|
||||
import copy
|
||||
import uuid
|
||||
import zoneinfo
|
||||
from datetime import time
|
||||
|
||||
import pytest
|
||||
from django.utils.timezone import now
|
||||
|
||||
from pretix.base.models import CachedFile
|
||||
from pretix.base.models import CachedFile, User
|
||||
|
||||
SAMPLE_EXPORTER_CONFIG = {
|
||||
"identifier": "orderlist",
|
||||
@@ -277,3 +280,532 @@ def test_org_level_export(token_client, organizer, team, event):
|
||||
'_format': 'xlsx',
|
||||
}, format='json')
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def event_scheduled_export(event, user):
|
||||
e = event.scheduled_exports.create(
|
||||
owner=user,
|
||||
export_identifier="orderlist",
|
||||
export_form_data={
|
||||
"_format": "xlsx",
|
||||
"date_range": "year_this"
|
||||
},
|
||||
locale="en",
|
||||
mail_additional_recipients="foo@example.org",
|
||||
mail_subject="Current order list",
|
||||
mail_template="Here is the current order list",
|
||||
schedule_rrule="DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
schedule_rrule_time=time(4, 0, 0),
|
||||
)
|
||||
e.compute_next_run()
|
||||
e.save()
|
||||
return e
|
||||
|
||||
|
||||
TEST_SCHEDULED_EXPORT_RES = {
|
||||
"owner": "dummy@dummy.dummy",
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "foo@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
"error_counter": 0,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_scheduled_export_list_token(token_client, organizer, event, user, team, event_scheduled_export):
|
||||
res = dict(TEST_SCHEDULED_EXPORT_RES)
|
||||
res["id"] = event_scheduled_export.pk
|
||||
res["schedule_next_run"] = event_scheduled_export.schedule_next_run.astimezone(zoneinfo.ZoneInfo("UTC")). \
|
||||
isoformat().replace("+00:00", "Z")
|
||||
|
||||
# Token can see it because it has change permission
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/scheduled_exports/'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 200
|
||||
assert [res] == resp.data['results']
|
||||
|
||||
team.can_change_event_settings = False
|
||||
team.save()
|
||||
|
||||
# Token can no longer sees it an gets error message
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/scheduled_exports/'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_scheduled_export_list_user(user_client, organizer, event, user, team, event_scheduled_export):
|
||||
user2 = User.objects.create_user('dummy2@dummy.dummy', 'dummy')
|
||||
team.members.add(user2)
|
||||
|
||||
res = dict(TEST_SCHEDULED_EXPORT_RES)
|
||||
res["id"] = event_scheduled_export.pk
|
||||
res["schedule_next_run"] = event_scheduled_export.schedule_next_run.astimezone(zoneinfo.ZoneInfo("UTC")).\
|
||||
isoformat().replace("+00:00", "Z")
|
||||
|
||||
# User can see it because its their own
|
||||
resp = user_client.get('/api/v1/organizers/{}/events/{}/scheduled_exports/'.format(organizer.slug, event.slug))
|
||||
assert [res] == resp.data['results']
|
||||
|
||||
team.can_change_event_settings = False
|
||||
team.save()
|
||||
|
||||
# Owner still can
|
||||
resp = user_client.get('/api/v1/organizers/{}/events/{}/scheduled_exports/'.format(organizer.slug, event.slug))
|
||||
assert [res] == resp.data['results']
|
||||
|
||||
# Other user can't see it and gets empty list
|
||||
user_client.force_authenticate(user=user2)
|
||||
resp = user_client.get('/api/v1/organizers/{}/events/{}/scheduled_exports/'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 200
|
||||
assert [] == resp.data['results']
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_scheduled_export_detail(token_client, organizer, event, user, event_scheduled_export):
|
||||
res = dict(TEST_SCHEDULED_EXPORT_RES)
|
||||
res["id"] = event_scheduled_export.pk
|
||||
res["schedule_next_run"] = event_scheduled_export.schedule_next_run.astimezone(zoneinfo.ZoneInfo("UTC")).\
|
||||
isoformat().replace("+00:00", "Z")
|
||||
|
||||
resp = token_client.get(
|
||||
'/api/v1/organizers/{}/events/{}/scheduled_exports/{}/'.format(
|
||||
organizer.slug, event.slug, event_scheduled_export.pk
|
||||
)
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert res == resp.data
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_scheduled_export_create(user_client, organizer, event, user):
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/scheduled_exports/'.format(organizer.slug, event.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "foo@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
created = event.scheduled_exports.get(id=resp.data["id"])
|
||||
assert created.export_form_data == {"_format": "xlsx", "date_range": "year_this"}
|
||||
assert created.owner == user
|
||||
assert created.schedule_next_run > now()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_scheduled_export_create_requires_user(token_client, organizer, event, user):
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/scheduled_exports/'.format(organizer.slug, event.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "foo@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_scheduled_export_delete_token(token_client, organizer, event, user, event_scheduled_export):
|
||||
resp = token_client.delete(
|
||||
'/api/v1/organizers/{}/events/{}/scheduled_exports/{}/'.format(
|
||||
organizer.slug, event.slug, event_scheduled_export.pk,
|
||||
),
|
||||
)
|
||||
assert resp.status_code == 204
|
||||
assert not event.scheduled_exports.exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_scheduled_export_update_token(token_client, organizer, event, user, event_scheduled_export):
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/scheduled_exports/{}/'.format(
|
||||
organizer.slug, event.slug, event_scheduled_export.pk,
|
||||
),
|
||||
data={
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "month_this"},
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
created = event.scheduled_exports.get(id=resp.data["id"])
|
||||
assert created.export_form_data == {"_format": "xlsx", "date_range": "month_this"}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def org_scheduled_export(organizer, user):
|
||||
e = organizer.scheduled_exports.create(
|
||||
owner=user,
|
||||
export_identifier="orderlist",
|
||||
export_form_data={
|
||||
"_format": "xlsx",
|
||||
"date_range": "year_this"
|
||||
},
|
||||
locale="en",
|
||||
mail_additional_recipients="foo@example.org",
|
||||
mail_subject="Current order list",
|
||||
mail_template="Here is the current order list",
|
||||
schedule_rrule="DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
schedule_rrule_time=time(4, 0, 0),
|
||||
)
|
||||
e.compute_next_run()
|
||||
e.save()
|
||||
return e
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_list_token(token_client, organizer, user, team, org_scheduled_export):
|
||||
res = dict(TEST_SCHEDULED_EXPORT_RES)
|
||||
res["id"] = org_scheduled_export.pk
|
||||
res["schedule_next_run"] = org_scheduled_export.schedule_next_run.astimezone(zoneinfo.ZoneInfo("UTC")). \
|
||||
isoformat().replace("+00:00", "Z")
|
||||
res["timezone"] = "UTC"
|
||||
|
||||
# Token can see it because it has change permission
|
||||
resp = token_client.get('/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug))
|
||||
assert resp.status_code == 200
|
||||
assert [res] == resp.data['results']
|
||||
|
||||
team.can_change_organizer_settings = False
|
||||
team.save()
|
||||
|
||||
# Token can no longer sees it an gets error message
|
||||
resp = token_client.get('/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug))
|
||||
assert resp.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_list_user(user_client, organizer, user, team, org_scheduled_export):
|
||||
user2 = User.objects.create_user('dummy2@dummy.dummy', 'dummy')
|
||||
team.members.add(user2)
|
||||
|
||||
res = dict(TEST_SCHEDULED_EXPORT_RES)
|
||||
res["id"] = org_scheduled_export.pk
|
||||
res["schedule_next_run"] = org_scheduled_export.schedule_next_run.astimezone(zoneinfo.ZoneInfo("UTC")). \
|
||||
isoformat().replace("+00:00", "Z")
|
||||
res["timezone"] = "UTC"
|
||||
|
||||
# User can see it because its their own
|
||||
resp = user_client.get('/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug))
|
||||
assert [res] == resp.data['results']
|
||||
|
||||
team.can_change_organizer_settings = False
|
||||
team.save()
|
||||
|
||||
# Owner still can
|
||||
resp = user_client.get('/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug))
|
||||
assert [res] == resp.data['results']
|
||||
|
||||
# Other user can't see it and gets empty list
|
||||
user_client.force_authenticate(user=user2)
|
||||
resp = user_client.get('/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug))
|
||||
assert resp.status_code == 200
|
||||
assert [] == resp.data['results']
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_detail(token_client, organizer, user, org_scheduled_export):
|
||||
res = dict(TEST_SCHEDULED_EXPORT_RES)
|
||||
res["id"] = org_scheduled_export.pk
|
||||
res["schedule_next_run"] = org_scheduled_export.schedule_next_run.astimezone(zoneinfo.ZoneInfo("UTC")). \
|
||||
isoformat().replace("+00:00", "Z")
|
||||
res["timezone"] = "UTC"
|
||||
|
||||
resp = token_client.get(
|
||||
'/api/v1/organizers/{}/scheduled_exports/{}/'.format(
|
||||
organizer.slug, org_scheduled_export.pk
|
||||
)
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert res == resp.data
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_create(user_client, organizer, user):
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "foo@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
created = organizer.scheduled_exports.get(id=resp.data["id"])
|
||||
assert created.export_form_data == {"_format": "xlsx", "date_range": "year_this", "event_date_range": "/"}
|
||||
assert created.owner == user
|
||||
assert created.schedule_next_run > now()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_create_requires_user(token_client, organizer, user):
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "foo@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_delete_token(token_client, organizer, user, org_scheduled_export):
|
||||
resp = token_client.delete(
|
||||
'/api/v1/organizers/{}/scheduled_exports/{}/'.format(
|
||||
organizer.slug, org_scheduled_export.pk,
|
||||
),
|
||||
)
|
||||
assert resp.status_code == 204
|
||||
assert not organizer.scheduled_exports.exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_update_token(token_client, organizer, user, org_scheduled_export):
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/scheduled_exports/{}/'.format(
|
||||
organizer.slug, org_scheduled_export.pk,
|
||||
),
|
||||
data={
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "month_this"},
|
||||
"timezone": "America/New_York"
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
created = organizer.scheduled_exports.get(id=resp.data["id"])
|
||||
assert created.export_form_data == {"_format": "xlsx", "date_range": "month_this", "event_date_range": "/"}
|
||||
assert created.timezone == "America/New_York"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_validate_identifier(user_client, organizer, user):
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug),
|
||||
data={
|
||||
"export_identifier": "unknownorg",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "foo@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"export_identifier": ["\"unknownorg\" is not a valid choice."]}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_validate_form_data(user_client, organizer, user):
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "UNKNOWN"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "foo@example.org",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"export_form_data": {"date_range": ["Invalid date frame"]}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_validate_locale(user_client, organizer, user):
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "BLÖDSINN",
|
||||
"mail_additional_recipients": "",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"locale": ["\"BLÖDSINN\" is not a valid choice."]}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_validate_timezone(user_client, organizer, user):
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "de",
|
||||
"mail_additional_recipients": "",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
"timezone": "Invalid"
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"timezone": ["\"Invalid\" is not a valid choice."]}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_validate_additional_recipients(user_client, organizer, user):
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "aaaaaa",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"mail_additional_recipients": ["Enter a valid email address."]}
|
||||
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,"
|
||||
"a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,"
|
||||
"a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,a@b.com,a@b.com",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"mail_additional_recipients": ["Please enter less than 25 recipients."]}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_scheduled_export_validate_rrule(user_client, organizer, user):
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "invalid content",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"schedule_rrule": ["Not a valid rrule."]}
|
||||
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH\nEXRULE:FREQ=WEEKLY;COUNT=4;INTERVAL=2;BYDAY=TU,TH",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"schedule_rrule": ["Only a single RRULE is allowed, no combination of rules."]}
|
||||
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/scheduled_exports/'.format(organizer.slug),
|
||||
data={
|
||||
"export_identifier": "orderlist",
|
||||
"export_form_data": {"_format": "xlsx", "date_range": "year_this"},
|
||||
"locale": "en",
|
||||
"mail_additional_recipients": "",
|
||||
"mail_additional_recipients_cc": "",
|
||||
"mail_additional_recipients_bcc": "",
|
||||
"mail_subject": "Current order list",
|
||||
"mail_template": "Here is the current order list",
|
||||
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=YEARLY;BYEASTER=0",
|
||||
"schedule_rrule_time": "04:00:00",
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"schedule_rrule": ["BYEASTER not supported"]}
|
||||
|
||||
@@ -277,6 +277,7 @@ TEST_ITEM_RES = {
|
||||
"min_per_order": None,
|
||||
"max_per_order": None,
|
||||
"hidden_if_available": None,
|
||||
"hidden_if_item_available": None,
|
||||
"checkin_attention": False,
|
||||
"has_variations": False,
|
||||
"require_approval": False,
|
||||
@@ -285,6 +286,7 @@ TEST_ITEM_RES = {
|
||||
"bundles": [],
|
||||
"show_quota_left": None,
|
||||
"original_price": None,
|
||||
"free_price_suggestion": None,
|
||||
"meta_data": {
|
||||
"day": "Tuesday"
|
||||
},
|
||||
@@ -384,6 +386,7 @@ def test_item_detail_variations(token_client, organizer, event, team, item):
|
||||
"id": var.pk,
|
||||
"value": {"en": "Children"},
|
||||
"default_price": None,
|
||||
"free_price_suggestion": None,
|
||||
"price": "23.00",
|
||||
"active": True,
|
||||
"description": None,
|
||||
@@ -1313,6 +1316,7 @@ TEST_VARIATIONS_RES = {
|
||||
"available_until": None,
|
||||
"hide_without_voucher": False,
|
||||
"original_price": None,
|
||||
"free_price_suggestion": None,
|
||||
"meta_data": {}
|
||||
}
|
||||
|
||||
@@ -1334,6 +1338,7 @@ TEST_VARIATIONS_UPDATE = {
|
||||
"available_until": None,
|
||||
"hide_without_voucher": False,
|
||||
"original_price": None,
|
||||
"free_price_suggestion": None,
|
||||
"meta_data": {}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ def test_full_clone_same_organizer():
|
||||
# todo: test that item pictures are copied, not linked
|
||||
ItemMetaValue.objects.create(item=item1, property=item_meta, value="Foo")
|
||||
assert item1.meta_data
|
||||
item2 = event.items.create(category=category, tax_rule=tax_rule, name="T-shirt", default_price=15)
|
||||
item2 = event.items.create(category=category, tax_rule=tax_rule, name="T-shirt", default_price=15,
|
||||
hidden_if_item_available=item1)
|
||||
item2v = item2.variations.create(value="red", default_price=15)
|
||||
item2v.meta_values.create(property=item_meta, value="Bar")
|
||||
item2.require_membership_types.add(membership_type)
|
||||
@@ -156,6 +157,7 @@ def test_full_clone_same_organizer():
|
||||
assert copied_item1.addons.get().addon_category == copied_event.categories.get()
|
||||
assert copied_item1.bundles.get().bundled_item == copied_item2
|
||||
assert copied_item1.bundles.get().bundled_variation == copied_item2.variations.get()
|
||||
assert copied_item2.hidden_if_item_available == copied_item1
|
||||
assert copied_q1.items.get() == copied_item1
|
||||
assert copied_q2.items.get() == copied_item2
|
||||
assert copied_q2.variations.get() == copied_item2.variations.get()
|
||||
|
||||
@@ -3336,6 +3336,33 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase):
|
||||
assert 'Workshop 1' in response.content.decode()
|
||||
assert 'Workshop 2' in response.content.decode()
|
||||
|
||||
def test_set_addons_hidden_if_item_available(self):
|
||||
with scopes_disabled():
|
||||
self.workshopquota2 = Quota.objects.create(event=self.event, name='Workshop 1', size=5)
|
||||
self.workshopquota2.items.add(self.workshop2)
|
||||
self.workshopquota2.variations.add(self.workshop2a)
|
||||
self.workshop2.hidden_if_item_available = self.workshop1
|
||||
self.workshop2.save()
|
||||
|
||||
ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1)
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
price=23, expires=now() - timedelta(minutes=10)
|
||||
)
|
||||
|
||||
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
|
||||
self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug),
|
||||
target_status_code=200)
|
||||
assert 'Workshop 1' in response.content.decode()
|
||||
assert 'Workshop 2' not in response.content.decode()
|
||||
|
||||
self.workshopquota.size = 0
|
||||
self.workshopquota.save()
|
||||
|
||||
response = self.client.get('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), follow=True)
|
||||
assert 'Workshop 1' in response.content.decode()
|
||||
assert 'Workshop 2' in response.content.decode()
|
||||
|
||||
def test_set_addons_subevent(self):
|
||||
with scopes_disabled():
|
||||
self.event.has_subevents = True
|
||||
|
||||
@@ -631,6 +631,30 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
|
||||
self.assertNotIn("SOLD OUT", doc.select("section:nth-of-type(1)")[0].text)
|
||||
self.assertIn("Late-bird", doc.select("section:nth-of-type(1)")[0].text)
|
||||
|
||||
def test_hidden_if_item_available(self):
|
||||
with scopes_disabled():
|
||||
q = Quota.objects.create(event=self.event, name='Early-bird', size=10)
|
||||
q2 = Quota.objects.create(event=self.event, name='Late-bird', size=10)
|
||||
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=12)
|
||||
item2 = Item.objects.create(event=self.event, name='Late-bird ticket', default_price=12,
|
||||
hidden_if_item_available=item)
|
||||
q.items.add(item)
|
||||
q2.items.add(item2)
|
||||
self.event.settings.hide_sold_out = True
|
||||
|
||||
doc = self.get_doc('/%s/%s/' % (self.orga.slug, self.event.slug))
|
||||
self.assertIn("Early-bird", doc.select("section:nth-of-type(1)")[0].text)
|
||||
self.assertNotIn("SOLD OUT", doc.select("section:nth-of-type(1)")[0].text)
|
||||
self.assertNotIn("Late-bird", doc.select("section:nth-of-type(1)")[0].text)
|
||||
|
||||
q.size = 0
|
||||
q.save()
|
||||
|
||||
doc = self.get_doc('/%s/%s/' % (self.orga.slug, self.event.slug))
|
||||
self.assertNotIn("Early-bird", doc.select("section:nth-of-type(1)")[0].text)
|
||||
self.assertNotIn("SOLD OUT", doc.select("section:nth-of-type(1)")[0].text)
|
||||
self.assertIn("Late-bird", doc.select("section:nth-of-type(1)")[0].text)
|
||||
|
||||
def test_bundle_sold_out(self):
|
||||
with scopes_disabled():
|
||||
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
||||
|
||||
@@ -1434,6 +1434,53 @@ class OrderChangeAddonsTest(BaseOrdersTest):
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.total == Decimal('35.00')
|
||||
|
||||
def test_allow_user_price_gte_paid(self):
|
||||
self.event.settings.change_allow_user_price = 'gte_paid'
|
||||
with scopes_disabled():
|
||||
OrderPosition.objects.create(
|
||||
order=self.order,
|
||||
item=self.workshop1,
|
||||
variation=None,
|
||||
price=Decimal("12"),
|
||||
addon_to=self.ticket_pos,
|
||||
attendee_name_parts={'full_name': "Peter"}
|
||||
)
|
||||
self.order.total += Decimal("12")
|
||||
self.order.save()
|
||||
self.order.payments.create(amount=Decimal("23"), provider="manual", state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
||||
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
||||
{
|
||||
},
|
||||
follow=True
|
||||
)
|
||||
assert 'alert-danger' not in response.content.decode()
|
||||
|
||||
with scopes_disabled():
|
||||
self.order.payments.create(amount=Decimal("12"), provider="manual", state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
||||
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
||||
{
|
||||
},
|
||||
follow=True
|
||||
)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
assert 'refund' in response.content.decode()
|
||||
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
||||
{
|
||||
'confirm': 'true'
|
||||
},
|
||||
follow=True
|
||||
)
|
||||
assert 'alert-danger' in response.content.decode()
|
||||
assert 'refund' in response.content.decode()
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.total == Decimal('35.00')
|
||||
|
||||
def test_allow_user_price_eq(self):
|
||||
self.event.settings.change_allow_user_price = 'eq'
|
||||
response = self.client.post(
|
||||
|
||||
@@ -184,6 +184,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"order_min": None,
|
||||
"max_price": None,
|
||||
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"picture": None,
|
||||
"picture_fullsize": None,
|
||||
"has_variations": 0,
|
||||
@@ -204,6 +205,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"order_min": None,
|
||||
"max_price": "14.00",
|
||||
"price": None,
|
||||
"suggested_price": None,
|
||||
"picture": None,
|
||||
"picture_fullsize": None,
|
||||
"has_variations": 4,
|
||||
@@ -219,6 +221,8 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
'original_price': None,
|
||||
"price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "",
|
||||
"rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"suggested_price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "",
|
||||
"rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"description": None,
|
||||
"avail": [100, None],
|
||||
"order_max": 2
|
||||
@@ -229,6 +233,8 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
'original_price': None,
|
||||
"price": {"gross": "12.00", "net": "10.08", "tax": "1.92", "name": "",
|
||||
"rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"suggested_price": {"gross": "12.00", "net": "10.08", "tax": "1.92", "name": "",
|
||||
"rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"description": None,
|
||||
"avail": [100, None],
|
||||
"order_max": 2
|
||||
@@ -266,6 +272,8 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"max_price": None,
|
||||
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00",
|
||||
"includes_mixed_tax_rate": False},
|
||||
"suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00",
|
||||
"includes_mixed_tax_rate": False},
|
||||
"picture": None,
|
||||
"picture_fullsize": None,
|
||||
"has_variations": 0,
|
||||
@@ -312,6 +320,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"order_min": None,
|
||||
"max_price": "14.00",
|
||||
"price": None,
|
||||
"suggested_price": None,
|
||||
"picture": None,
|
||||
"picture_fullsize": None,
|
||||
"has_variations": 4,
|
||||
@@ -327,6 +336,8 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
'original_price': None,
|
||||
"price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "",
|
||||
"rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"suggested_price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "",
|
||||
"rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"description": None,
|
||||
"avail": [100, None],
|
||||
"order_max": 2
|
||||
@@ -374,6 +385,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"order_min": None,
|
||||
"max_price": None,
|
||||
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"picture": None,
|
||||
"picture_fullsize": None,
|
||||
"has_variations": 0,
|
||||
@@ -439,6 +451,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
'order_min': None,
|
||||
'order_max': None,
|
||||
'price': None,
|
||||
'suggested_price': None,
|
||||
'min_price': '14.00',
|
||||
'max_price': '14.00',
|
||||
'free_price': False,
|
||||
@@ -459,6 +472,14 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
'name': '',
|
||||
'includes_mixed_tax_rate': False
|
||||
},
|
||||
'suggested_price': {
|
||||
'gross': '14.00',
|
||||
'net': '11.76',
|
||||
'tax': '2.24',
|
||||
'rate': '19.00',
|
||||
'name': '',
|
||||
'includes_mixed_tax_rate': False
|
||||
},
|
||||
'avail': [100, None]
|
||||
},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user