mirror of
https://github.com/pretix/pretix.git
synced 2026-06-19 02:36:15 +00:00
Compare commits
317 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f2cab689af | |||
| c17a090244 | |||
| d103d8782b | |||
| 4f4903b00e | |||
| caf291630c | |||
| 6d0368a1bb | |||
| 57d33e1eb1 | |||
| e6ec4cb435 | |||
| 6043a96575 | |||
| 5bc1fb8e81 | |||
| 47c840b9e5 | |||
| b6007a1af4 | |||
| 1caa71cdbe | |||
| 1f2a0278c0 | |||
| cf51c879c7 | |||
| 1030e2dc1f | |||
| 8d320b24a5 | |||
| 8235132de8 | |||
| 2614f12faf | |||
| 6f92f2324f | |||
| aaef7579d9 | |||
| 6b1077f881 | |||
| 6154b7fae0 | |||
| f43be3079f | |||
| 3abe82ec77 | |||
| 7b30902963 | |||
| a885c8d2e5 | |||
| 0ae98f072a | |||
| 9b1a723001 | |||
| 46d7799cd0 | |||
| fcb67ec4b5 | |||
| c7565e7c8b | |||
| 2316cb557a | |||
| 4de75f3ba5 | |||
| cb972cd6ca | |||
| 3354ccf78a | |||
| f17038101c | |||
| 8e343898b4 | |||
| 09dc504c87 | |||
| d780d1d25c | |||
| 5522d67f9b | |||
| 7472564c26 | |||
| 2b735bec0b | |||
| 3e335bcbfe | |||
| f676a77536 | |||
| c487373340 | |||
| 3e05463486 | |||
| 31bb0f4a91 | |||
| 2605fe93d9 | |||
| 3dedfd6ee0 | |||
| f71eb195c4 | |||
| f09e9590a8 | |||
| c53d44238c | |||
| 3bcc504bd8 | |||
| d802f747c7 | |||
| d926030bf6 | |||
| 1e4c577b31 | |||
| 8cae00941a | |||
| 602287b3ec | |||
| 817f17dac5 | |||
| f33dd84900 | |||
| 99d0ca314d | |||
| 853749521e | |||
| df5c6bcebf | |||
| 3e6051825e | |||
| f4478da5ce | |||
| 013a065132 | |||
| b3919973f1 | |||
| e1027e3e8c | |||
| d3792935ae | |||
| 1804dbebd0 | |||
| 7ca2f8ec04 | |||
| 15eb0f8870 | |||
| 01228dd865 | |||
| bbde731dca | |||
| b21ea1ba9a | |||
| ef1220d240 | |||
| cb3514a14f | |||
| 117a66a837 | |||
| 384d0c4824 | |||
| 667443ab56 | |||
| 22e99cf246 | |||
| 3e3cabe2bb | |||
| ca12cbb69e | |||
| 21c273854c | |||
| 0eb11b154b | |||
| 8796b4359c | |||
| 698f687c54 | |||
| ec627d6a3c | |||
| a252c69988 | |||
| 578518e8ab | |||
| d50d6a1dfd | |||
| 9d01072880 | |||
| 48c2d57cd4 | |||
| 2b245f727e | |||
| f051ddca2d | |||
| 2ba2f9ff4b | |||
| 4ceb617104 | |||
| e1d2721747 | |||
| 515f79b206 | |||
| 025ee6710c | |||
| 94671f6f70 | |||
| c1656158f2 | |||
| d1bd719f66 | |||
| d61aed105f | |||
| 4bf4cd748c | |||
| 6b58c1484c | |||
| 6cc7a2a0de | |||
| c6862454f5 | |||
| 6d28a7f384 | |||
| 15251ff208 | |||
| fea519962c | |||
| 929a2eb6e3 | |||
| 08eabfa61c | |||
| 66dfa99e58 | |||
| 3b0cd35c7a | |||
| 810bdff5d9 | |||
| 54e02da2b3 | |||
| 86133ee52f | |||
| 1e3b924998 | |||
| ac2b1186d1 | |||
| 9608c7aa15 | |||
| 2bb324f885 | |||
| ea955c779e | |||
| 9844ffca98 | |||
| 12f4473fbd | |||
| 8c3ac46ddf | |||
| b4d8e9ccc4 | |||
| 899994ef1e | |||
| 277a5bffa8 | |||
| 1bbefddc11 | |||
| 957462739a | |||
| fa468366c7 | |||
| 9a61de9a22 | |||
| 17df83a9fb | |||
| f3bd918846 | |||
| 5768fbca54 | |||
| d88e47d76b | |||
| d8ec489b13 | |||
| 804b048dbb | |||
| a3d721c08b | |||
| b5544b120d | |||
| 5138e86cf1 | |||
| f455152447 | |||
| 9447e5802d | |||
| e5fc7144e4 | |||
| a7c8bb0f02 | |||
| fbb6246020 | |||
| d39a01af4d | |||
| d928adf7a3 | |||
| 4755200ab0 | |||
| 0b8a7349c7 | |||
| 1a1948e3fa | |||
| f70874b21c | |||
| 1c8bcca846 | |||
| 6c7041c875 | |||
| 8c9a94f87e | |||
| 6f3c2ed444 | |||
| 736792fc01 | |||
| bacb40fd7f | |||
| 42399c3488 | |||
| 592b98b83a | |||
| 83c333c798 | |||
| ff76fe38e3 | |||
| 8dff640d54 | |||
| 339820a5a0 | |||
| 47990dc1d6 | |||
| 4db9b719ed | |||
| 873a2d02df | |||
| 8ab94bb294 | |||
| a903cfa4d1 | |||
| 06efb37d5d | |||
| 533698c33f | |||
| d7d946878c | |||
| 633ff804a8 | |||
| e5997d3a14 | |||
| d1faee0b8f | |||
| 41e8ab7717 | |||
| 3a3f45a988 | |||
| b4ead8f814 | |||
| 19cf34b42d | |||
| 8ec8b69755 | |||
| de9a86c614 | |||
| 5375e22781 | |||
| 6a92b98766 | |||
| 999055f082 | |||
| b969f114f5 | |||
| 695e817f99 | |||
| b7caf7769e | |||
| 67b65bf67d | |||
| 9ca0e41c5f | |||
| d562a2858d | |||
| 728c6d30a3 | |||
| 1bbbfb5cf9 | |||
| c9dd65fba8 | |||
| f5989cb2ff | |||
| a27af1373b | |||
| 5e2633efa8 | |||
| 0144c6341e | |||
| 9a57aa67b2 | |||
| 7652f48dc1 | |||
| a22cc56944 | |||
| 322329d956 | |||
| 2186a17530 | |||
| 3b632728e3 | |||
| addba1ff57 | |||
| 4740291e43 | |||
| 745929b625 | |||
| 7da03ac17c | |||
| ac8cb3bfd1 | |||
| 0236911a88 | |||
| f475781a89 | |||
| fcd8c82092 | |||
| 9ec9c67550 | |||
| 3eff74bf4c | |||
| d0d17a1d0a | |||
| 0cca053d45 | |||
| 9581457d2f | |||
| 7475a5d7b1 | |||
| e211f006e3 | |||
| 924aa3ac51 | |||
| 6bfd61b789 | |||
| 6c71fa8c5a | |||
| 945f5e9dcb | |||
| 7c18d46760 | |||
| 52020bc866 | |||
| 2cec516ad7 | |||
| 44aad7834c | |||
| 92ebcddbbb | |||
| d2f694b260 | |||
| ea3b557fcc | |||
| 8ea03b54a3 | |||
| 6dae8ef6b7 | |||
| 502ebcbc27 | |||
| 19a2f7b0d3 | |||
| 7c9a0343e5 | |||
| f006b765f2 | |||
| de71754d9f | |||
| 844547d356 | |||
| afb939c622 | |||
| ccefca5d0c | |||
| a2252fb021 | |||
| 16b7e28074 | |||
| e0957639ad | |||
| 392e31837e | |||
| 51271463fb | |||
| 80e289f67e | |||
| 79d420fa9b | |||
| e6ac66ff19 | |||
| 495aaa762c | |||
| 43d967fe60 | |||
| 8a9060da7c | |||
| fc4b46a140 | |||
| 0c27ac5d27 | |||
| 12da2666db | |||
| 03bdfe3540 | |||
| 8bce847054 | |||
| c8cdf0438f | |||
| 42b432daa2 | |||
| 88bc144028 | |||
| 032be74d77 | |||
| e2e811ea47 | |||
| febf32a0a4 | |||
| 0a63ae67d3 | |||
| da4438bbf7 | |||
| 6dc5f5bc99 | |||
| a6d3139e25 | |||
| 7348c1a3d4 | |||
| 16811a1e63 | |||
| de3fd54a27 | |||
| 201def6aae | |||
| e38ff7021d | |||
| b9c94f21af | |||
| 6d9edea05e | |||
| d9e8dd70e4 | |||
| 0e17ac6ea5 | |||
| 1bdec48552 | |||
| a7e92657b0 | |||
| 5bf9980e8a | |||
| e236dc275d | |||
| aa38163096 | |||
| e56eb2df4f | |||
| d851885d16 | |||
| a2955b4579 | |||
| a7cd739555 | |||
| d9d5795837 | |||
| 886f7f6178 | |||
| b66bbf7936 | |||
| cb4ca80704 | |||
| bf7b0d6c21 | |||
| 5cd97d6021 | |||
| 15c3b44395 | |||
| 75b703b62f | |||
| 3446986434 | |||
| 05dc2a1c19 | |||
| 631771aa4b | |||
| de142e1c97 | |||
| d5fd5edf11 | |||
| c6356b439b | |||
| 0381f94841 | |||
| a4b69b9d9f | |||
| 79975e6174 | |||
| 012eda66bd | |||
| 018569a0d6 | |||
| 625bc66516 | |||
| 946777bd64 | |||
| 982069720e | |||
| f450abba24 | |||
| 11632e082f | |||
| 083e5ed265 | |||
| b48259ab60 | |||
| acf200da39 | |||
| b46db4b47a | |||
| 5cacb40d96 | |||
| efac98e98d | |||
| a56033bfb0 | |||
| dfdeaabb71 |
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"contributors": "https://crm.rami.io/cla/check/?project=pretix&checkContributor=",
|
||||
"message": "Hey there! :) Thank you very much for offering a contribution to pretix! For legal reasons, we need you to sign a Contributor License Agreement in order to be able to merge the code. Sorry for the hassle :( Please download the agreement from https://pretix.eu/about/en/cla and send a signed copy to support@pretix.eu. Feel free to also contact us there or via comments here if you have any questions!",
|
||||
"message": "Hey there! :) Thank you very much for offering a contribution to pretix! For legal reasons, we need you to sign a Contributor License Agreement in order to be able to merge the code. Sorry for the hassle :( Please download the agreement from https://pretix.eu/about/en/cla and send a signed copy to support@pretix.eu. Feel free to also contact us there or via comments here if you have any questions!\n\nFeel free to ignore me on documentation changes, translations, and trivial PRs like typo fixes (and similar single-line changes) – we can merge these without a CLA as well.",
|
||||
"label": "cla-signed"
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
Code of Conduct
|
||||
===============
|
||||
|
||||
We have a [Code of Conduct](https://docs.pretix.eu/en/latest/development/contribution/codeofconduct.html)
|
||||
We have a [Code of Conduct](https://docs.pretix.eu/dev/development/contribution/codeofconduct.html)
|
||||
in place that applies to all project contributions, including issues, pull requests, etc.
|
||||
|
||||
+3
-3
@@ -3,9 +3,9 @@ Contributing to pretix
|
||||
|
||||
Hey there and welcome to pretix!
|
||||
|
||||
* We've got a contributors guide in [our documentation](https://docs.pretix.eu/en/latest/development/contribution/) together with notes on the [development setup](https://docs.pretix.eu/en/latest/development/setup.html).
|
||||
* We've got a contributors guide in [our documentation](https://docs.pretix.eu/dev/development/contribution/) together with notes on the [development setup](https://docs.pretix.eu/dev/development/setup.html).
|
||||
|
||||
* Please note that we have a [Code of Conduct](https://docs.pretix.eu/en/latest/development/contribution/codeofconduct.html) in place that applies to all project contributions, including issues, pull requests, etc.
|
||||
* Please note that we have a [Code of Conduct](https://docs.pretix.eu/dev/development/contribution/codeofconduct.html) in place that applies to all project contributions, including issues, pull requests, etc.
|
||||
|
||||
* Before we can accept a PR from you we'll need you to sign [our CLA](https://pretix.eu/about/en/cla). You can find more information about the how and why in our [License FAQ](https://docs.pretix.eu/en/latest/license/faq.html#) and in our [license change blog post](https://pretix.eu/about/en/blog/20210412-license/).
|
||||
* Before we can accept a PR from you we'll need you to sign [our CLA](https://pretix.eu/about/en/cla). You can find more information about the how and why in our [License FAQ](https://docs.pretix.eu/trust/licensing/faq/) and in our [license change blog post](https://pretix.eu/about/en/blog/20210412-license/).
|
||||
|
||||
|
||||
+4
-4
@@ -5,7 +5,7 @@ pretix
|
||||
:target: https://pypi.python.org/pypi/pretix
|
||||
|
||||
.. image:: https://github.com/pretix/pretix/workflows/Documentation/badge.svg
|
||||
:target: https://docs.pretix.eu/en/latest/
|
||||
:target: https://docs.pretix.eu/
|
||||
|
||||
.. image:: https://github.com/pretix/pretix/workflows/Tests/badge.svg
|
||||
|
||||
@@ -56,8 +56,8 @@ License
|
||||
The code in this repository is covered by different licenses. Most of it is available to everyone under the terms of
|
||||
the GNU AGPL license v3 with additional terms. See the LICENSE file for the complete license details.
|
||||
|
||||
.. _installation guide: https://docs.pretix.eu/en/latest/admin/installation/index.html
|
||||
.. _developer documentation: https://docs.pretix.eu/en/latest/development/index.html
|
||||
.. _Code of Conduct: https://docs.pretix.eu/en/latest/development/contribution/codeofconduct.html
|
||||
.. _installation guide: https://docs.pretix.eu/self-hosting/installation/general/
|
||||
.. _developer documentation: https://docs.pretix.eu/dev/development/index.html
|
||||
.. _Code of Conduct: https://docs.pretix.eu/dev/development/contribution/codeofconduct.html
|
||||
.. _pretix.eu: https://pretix.eu
|
||||
.. _blog: https://pretix.eu/about/en/blog/
|
||||
|
||||
@@ -23,7 +23,7 @@ Certificate download
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/orderpositions/23442/download/certificate/ HTTP/1.1
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/orderpositions/23442/certificate/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
@@ -38,7 +38,7 @@ Certificate download
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/orderpositions/23442/download/certificate/?result=1f550651-ae7b-4911-a76c-2be8f348aaa5 HTTP/1.1
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/orderpositions/23442/certificate/?result=1f550651-ae7b-4911-a76c-2be8f348aaa5 HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
|
||||
@@ -54,6 +54,11 @@ Checking a ticket in
|
||||
this request twice with the same nonce, the second request will also succeed but will always
|
||||
create only one check-in object even when the previous request was successful as well. This
|
||||
allows for a certain level of idempotency and enables you to re-try after a connection failure.
|
||||
:<json boolean use_order_locale: Specifies that pretix should use the customer's language (``locale`` field from the
|
||||
order) when building texts (currently only the ``reason_explanation`` response field).
|
||||
Defaults to ``false`` in which case the server will determine the language (currently
|
||||
the event default language, might change in the future with support for the
|
||||
``Accept-Language`` header).
|
||||
:>json string status: ``"ok"``, ``"incomplete"``, or ``"error"``
|
||||
:>json string reason: Reason code, only set on status ``"error"``, see below for possible values.
|
||||
:>json string reason_explanation: Human-readable explanation, only set on status ``"error"`` and reason ``"rules"``, can be null.
|
||||
@@ -62,7 +67,9 @@ Checking a ticket in
|
||||
will only include check-ins for the selected list. (2) An additional boolean property
|
||||
``require_attention`` will inform you whether either the order or the item have the
|
||||
``checkin_attention`` flag set. (3) If ``attendee_name`` is empty, it may automatically fall
|
||||
back to values from a parent product or from invoice addresses.
|
||||
back to values from a parent product or from invoice addresses. (4) Additional properties
|
||||
``order__status``, ``order__valid_if_pending``, ``order__require_approval``, and
|
||||
``order__locale`` are included with details form the order for convenience.
|
||||
:>json boolean require_attention: Whether or not the ``require_attention`` flag is set on the item or order.
|
||||
:>json list checkin_texts: List of additional texts to show to the user.
|
||||
:>json object list: Excerpt of information about the matching :ref:`check-in list <rest-checkinlists>` (if any was found),
|
||||
|
||||
@@ -35,6 +35,10 @@ subevent_mode strings Determines h
|
||||
``"same"`` (discount is only applied for groups within
|
||||
the same date), or ``"distinct"`` (discount is only applied
|
||||
for groups with no two same dates).
|
||||
subevent_date_from datetime The first date time of a subevent to which this discount can be applied
|
||||
(or ``null``). Ignored in non-series events.
|
||||
subevent_date_until datetime The last date time of a subevent to which this discount can be applied
|
||||
(or ``null``). Ignored in non-series events.
|
||||
condition_all_products boolean If ``true``, the discount condition applies to all items.
|
||||
condition_limit_products list of integers If ``condition_all_products`` is not set, this is a list
|
||||
of internal item IDs that the discount condition applies to.
|
||||
@@ -105,6 +109,8 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"subevent_mode": "mixed",
|
||||
"subevent_date_from": null,
|
||||
"subevent_date_until": null,
|
||||
"condition_all_products": true,
|
||||
"condition_limit_products": [],
|
||||
"condition_apply_to_addons": true,
|
||||
@@ -163,6 +169,8 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"subevent_mode": "mixed",
|
||||
"subevent_date_from": null,
|
||||
"subevent_date_until": null,
|
||||
"condition_all_products": true,
|
||||
"condition_limit_products": [],
|
||||
"condition_apply_to_addons": true,
|
||||
@@ -207,6 +215,8 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"subevent_mode": "mixed",
|
||||
"subevent_date_from": null,
|
||||
"subevent_date_until": null,
|
||||
"condition_all_products": true,
|
||||
"condition_limit_products": [],
|
||||
"condition_apply_to_addons": true,
|
||||
@@ -240,6 +250,8 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"subevent_mode": "mixed",
|
||||
"subevent_date_from": null,
|
||||
"subevent_date_until": null,
|
||||
"condition_all_products": true,
|
||||
"condition_limit_products": [],
|
||||
"condition_apply_to_addons": true,
|
||||
@@ -302,6 +314,8 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"subevent_mode": "mixed",
|
||||
"subevent_date_from": null,
|
||||
"subevent_date_until": null,
|
||||
"condition_all_products": true,
|
||||
"condition_limit_products": [],
|
||||
"condition_apply_to_addons": true,
|
||||
|
||||
@@ -75,8 +75,9 @@ positions list of objects List of order p
|
||||
fees list of objects List of fees included in the order total. By default, only
|
||||
non-canceled fees are included.
|
||||
├ id integer Internal ID of the fee record
|
||||
├ fee_type string Type of fee (currently ``payment``, ``passbook``,
|
||||
``other``)
|
||||
├ fee_type string Type of fee (currently ``payment``, ``shipping``,
|
||||
``service``, ``cancellation``, ``insurance``, ``late``,
|
||||
``other``, ``giftcard``)
|
||||
├ value money (string) Fee amount
|
||||
├ description string Human-readable string with more details (can be empty)
|
||||
├ internal_type string Internal string (i.e. ID of the payment provider),
|
||||
@@ -1942,9 +1943,14 @@ Manipulating individual positions
|
||||
|
||||
* ``valid_until``
|
||||
|
||||
* ``secret``
|
||||
|
||||
Changing parameters such as ``item`` or ``price`` will **not** automatically trigger creation of a new invoice,
|
||||
you need to take care of that yourself.
|
||||
|
||||
Changing ``secret`` does not cause a new PDF ticket to be sent to the customer, nor does it cause the old secret
|
||||
to be added to the revocation list, even if your ticket generator uses one.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
@@ -2244,6 +2250,9 @@ otherwise, such as splitting an order or changing fees.
|
||||
|
||||
* ``cancel_fees``: A list of objects with the single key ``fee`` specifying an order fee ID.
|
||||
|
||||
* ``create_fees``: A list of objects describing new order fees with the fields ``fee_type``, ``value``, ``description``,
|
||||
``internal_type``, ``tax_rule``
|
||||
|
||||
* ``recalculate_taxes``: If set to ``"keep_net"``, all taxes will be recalculated based on the tax rule and invoice
|
||||
address, the net price will be kept. If set to ``"keep_gross"``, the gross price will be kept. If set to ``null``
|
||||
(the default) the taxes are not recalculated.
|
||||
@@ -2263,17 +2272,12 @@ otherwise, such as splitting an order or changing fees.
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"cancel_positions": [
|
||||
{
|
||||
"position": 12373
|
||||
}
|
||||
],
|
||||
"patch_positions": [
|
||||
{
|
||||
"position": 12374,
|
||||
"body": {
|
||||
"item": 12,
|
||||
"variation": None,
|
||||
"variation": null,
|
||||
"subevent": 562,
|
||||
"seat": "seat-guid-2",
|
||||
"price": "99.99",
|
||||
@@ -2281,6 +2285,11 @@ otherwise, such as splitting an order or changing fees.
|
||||
}
|
||||
}
|
||||
],
|
||||
"cancel_positions": [
|
||||
{
|
||||
"position": 12373
|
||||
}
|
||||
],
|
||||
"split_positions": [
|
||||
{
|
||||
"position": 12375
|
||||
@@ -2289,7 +2298,7 @@ otherwise, such as splitting an order or changing fees.
|
||||
"create_positions": [
|
||||
{
|
||||
"item": 12,
|
||||
"variation": None,
|
||||
"variation": null,
|
||||
"subevent": 562,
|
||||
"seat": "seat-guid-2",
|
||||
"price": "99.99",
|
||||
@@ -2297,12 +2306,7 @@ otherwise, such as splitting an order or changing fees.
|
||||
"attendee_name": "Peter",
|
||||
}
|
||||
],
|
||||
"cancel_fees": [
|
||||
{
|
||||
"fee": 49
|
||||
}
|
||||
],
|
||||
"change_fees": [
|
||||
"patch_fees": [
|
||||
{
|
||||
"fee": 51,
|
||||
"body": {
|
||||
@@ -2310,6 +2314,20 @@ otherwise, such as splitting an order or changing fees.
|
||||
}
|
||||
}
|
||||
],
|
||||
"cancel_fees": [
|
||||
{
|
||||
"fee": 49
|
||||
}
|
||||
],
|
||||
"create_fees": [
|
||||
{
|
||||
"fee_type": "other",
|
||||
"value": "1.50",
|
||||
"description": "Example Fee",
|
||||
"internal_type": "",
|
||||
"tax_rule": 15
|
||||
}
|
||||
],
|
||||
"reissue_invoice": true,
|
||||
"send_email": true,
|
||||
"recalculate_taxes": "keep_gross"
|
||||
|
||||
@@ -23,7 +23,7 @@ There are multiple signals that will be sent out in the ordering cycle:
|
||||
|
||||
.. automodule:: pretix.base.signals
|
||||
:no-index:
|
||||
:members: validate_cart, validate_cart_addons, validate_order, order_valid_if_pending, order_fee_calculation, order_paid, order_placed, order_canceled, order_reactivated, order_expired, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download, order_split, order_gracefully_delete, invoice_line_text
|
||||
:members: validate_cart, validate_cart_addons, validate_order, order_valid_if_pending, order_fee_calculation, order_paid, order_placed, order_canceled, order_reactivated, order_expired, order_expiry_changed, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download, order_split, order_gracefully_delete, invoice_line_text
|
||||
|
||||
Check-ins
|
||||
"""""""""
|
||||
|
||||
@@ -84,6 +84,8 @@ A working example would be:
|
||||
restricted = False
|
||||
description = _("This plugin allows you to receive payments via PayPal")
|
||||
compatibility = "pretix>=2.7.0"
|
||||
settings_links = []
|
||||
navigation_links = []
|
||||
|
||||
|
||||
default_app_config = 'pretix_paypal.PaypalApp'
|
||||
@@ -185,6 +187,28 @@ your Django app label.
|
||||
with checking that the calling user is logged in, has appropriate permissions,
|
||||
etc. We plan on providing native support for this in a later version.
|
||||
|
||||
To make your plugin views easily discoverable, you can specify links for "Go to"
|
||||
and "Settings" buttons next to your entry on the plugin page. These links should be
|
||||
added to the ``navigation_links`` and ``settings_links``, respectively, in the
|
||||
``PretixPluginMeta`` class.
|
||||
|
||||
Each array entry consists of a tuple ``(label, urlname, kwargs)``. For the label,
|
||||
either a string or a tuple of strings can be specified. In the latter case, the provided
|
||||
strings will be merged with a separator indicating they are successive navigation steps
|
||||
the user would need to take to reach the page via the regular menu
|
||||
(e.g. "Payment > Bank transfer" as below).
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
settings_links = [
|
||||
((_("Payment"), _("Bank transfer")), "control:event.settings.payment.provider", {"provider": "banktransfer"}),
|
||||
]
|
||||
navigation_links = [
|
||||
((_("Bank transfer"), _("Import bank data")), "plugins:banktransfer:import", {}),
|
||||
((_("Bank transfer"), _("Export refunds")), "plugins:banktransfer:refunds.list", {}),
|
||||
]
|
||||
|
||||
|
||||
.. _Django app: https://docs.djangoproject.com/en/3.0/ref/applications/
|
||||
.. _signal dispatcher: https://docs.djangoproject.com/en/3.0/topics/signals/
|
||||
.. _namespace packages: https://legacy.python.org/dev/peps/pep-0420/
|
||||
|
||||
+16
-16
@@ -30,13 +30,13 @@ dependencies = [
|
||||
"babel",
|
||||
"BeautifulSoup4==4.13.*",
|
||||
"bleach==6.2.*",
|
||||
"celery==5.4.*",
|
||||
"celery==5.5.*",
|
||||
"chardet==5.2.*",
|
||||
"cryptography>=44.0.0",
|
||||
"css-inline==0.14.*",
|
||||
"defusedcsv>=1.1.0",
|
||||
"Django[argon2]==4.2.*,>=4.2.15",
|
||||
"django-bootstrap3==24.3",
|
||||
"django-bootstrap3==25.1",
|
||||
"django-compressor==4.5.1",
|
||||
"django-countries==7.6.*",
|
||||
"django-filter==25.1",
|
||||
@@ -49,22 +49,22 @@ dependencies = [
|
||||
"django-localflavor==4.0",
|
||||
"django-markup",
|
||||
"django-oauth-toolkit==2.3.*",
|
||||
"django-otp==1.5.*",
|
||||
"django-otp==1.6.*",
|
||||
"django-phonenumber-field==7.3.*",
|
||||
"django-redis==5.4.*",
|
||||
"django-scopes==2.0.*",
|
||||
"django-statici18n==2.6.*",
|
||||
"djangorestframework==3.15.*",
|
||||
"djangorestframework==3.16.*",
|
||||
"dnspython==2.7.*",
|
||||
"drf_ujson2==1.7.*",
|
||||
"geoip2==5.*",
|
||||
"importlib_metadata==8.*", # Polyfill, we can probably drop this once we require Python 3.10+
|
||||
"isoweek",
|
||||
"jsonschema",
|
||||
"kombu==5.4.*",
|
||||
"kombu==5.5.*",
|
||||
"libsass==0.23.*",
|
||||
"lxml",
|
||||
"markdown==3.7", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
|
||||
"markdown==3.8", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
|
||||
# We can upgrade markdown again once django-bootstrap3 upgrades or once we drop Python 3.6 and 3.7
|
||||
"mt-940==4.30.*",
|
||||
"oauthlib==3.2.*",
|
||||
@@ -73,25 +73,25 @@ dependencies = [
|
||||
"paypalrestsdk==1.13.*",
|
||||
"paypal-checkout-serversdk==1.0.*",
|
||||
"PyJWT==2.10.*",
|
||||
"phonenumberslite==8.13.*",
|
||||
"Pillow==11.1.*",
|
||||
"phonenumberslite==9.0.*",
|
||||
"Pillow==11.2.*",
|
||||
"pretix-plugin-build",
|
||||
"protobuf==5.29.*",
|
||||
"protobuf==6.30.*",
|
||||
"psycopg2-binary",
|
||||
"pycountry",
|
||||
"pycparser==2.22",
|
||||
"pycryptodome==3.21.*",
|
||||
"pypdf==5.1.*",
|
||||
"pycryptodome==3.22.*",
|
||||
"pypdf==5.4.*",
|
||||
"python-bidi==0.6.*", # Support for Arabic in reportlab
|
||||
"python-dateutil==2.9.*",
|
||||
"pytz",
|
||||
"pytz-deprecation-shim==0.1.*",
|
||||
"pyuca",
|
||||
"qrcode==8.0",
|
||||
"qrcode==8.2",
|
||||
"redis==5.2.*",
|
||||
"reportlab==4.3.*",
|
||||
"reportlab==4.4.*",
|
||||
"requests==2.31.*",
|
||||
"sentry-sdk==2.22.*",
|
||||
"sentry-sdk==2.27.*",
|
||||
"sepaxml==2.6.*",
|
||||
"stripe==7.9.*",
|
||||
"text-unidecode==1.*",
|
||||
@@ -111,10 +111,10 @@ dev = [
|
||||
"coverage",
|
||||
"coveralls",
|
||||
"fakeredis==2.26.*",
|
||||
"flake8==7.1.*",
|
||||
"flake8==7.2.*",
|
||||
"freezegun",
|
||||
"isort==6.0.*",
|
||||
"pep8-naming==0.14.*",
|
||||
"pep8-naming==0.15.*",
|
||||
"potypo",
|
||||
"pytest-asyncio>=0.24",
|
||||
"pytest-cache",
|
||||
|
||||
@@ -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__ = "2025.2.0.dev0"
|
||||
__version__ = "2025.5.0.dev0"
|
||||
|
||||
@@ -121,6 +121,7 @@ LANGUAGES_OFFICIAL = {
|
||||
'en', 'de', 'de-informal'
|
||||
}
|
||||
LANGUAGES_RTL = {
|
||||
# When adding more right-to-left languages, also update pretix/static/pretixbase/scss/_rtl.scss
|
||||
'ar', 'hw'
|
||||
}
|
||||
LANGUAGES_INCUBATING = {
|
||||
@@ -259,7 +260,7 @@ COMPRESS_FILTERS = {
|
||||
CURRENCIES = [
|
||||
c for c in currencies
|
||||
if c.alpha_3 not in {
|
||||
'XAG', 'XAU', 'XBA', 'XBB', 'XBC', 'XBD', 'XDR', 'XPD', 'XPT', 'XSU', 'XTS', 'XUA',
|
||||
'USN', 'XAG', 'XAU', 'XBA', 'XBB', 'XBC', 'XBD', 'XDR', 'XPD', 'XPT', 'XSU', 'XTS', 'XUA',
|
||||
}
|
||||
]
|
||||
CURRENCY_PLACES = {
|
||||
|
||||
@@ -81,6 +81,13 @@ class SalesChannelMigrationMixin:
|
||||
|
||||
def to_internal_value(self, data):
|
||||
if "sales_channels" in data:
|
||||
if data["sales_channels"] is None:
|
||||
raise ValidationError({
|
||||
"sales_channels": [
|
||||
"The legacy attribute 'sales_channels' cannot be set to None, it must be a list."
|
||||
]
|
||||
})
|
||||
|
||||
prefetch_related_objects([self.organizer], "sales_channels")
|
||||
all_channels = {
|
||||
s.identifier for s in
|
||||
@@ -89,7 +96,7 @@ class SalesChannelMigrationMixin:
|
||||
|
||||
if data.get("all_sales_channels") and set(data["sales_channels"]) != all_channels:
|
||||
raise ValidationError({
|
||||
"limit_sales_channels": [
|
||||
"all_sales_channels": [
|
||||
"If 'all_sales_channels' is set, the legacy attribute 'sales_channels' must not be set or set to "
|
||||
"the list of all sales channels."
|
||||
]
|
||||
@@ -109,6 +116,7 @@ class SalesChannelMigrationMixin:
|
||||
else:
|
||||
data["all_sales_channels"] = False
|
||||
data["limit_sales_channels"] = data["sales_channels"]
|
||||
|
||||
del data["sales_channels"]
|
||||
|
||||
if data.get("all_sales_channels"):
|
||||
|
||||
@@ -176,7 +176,7 @@ class BaseCartPositionCreateSerializer(I18nAwareModelSerializer):
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data.pop('_quotas')
|
||||
answers_data = validated_data.pop('answers')
|
||||
answers_data = validated_data.pop('answers', [])
|
||||
|
||||
attendee_name = validated_data.pop('attendee_name', '')
|
||||
if attendee_name and not validated_data.get('attendee_name_parts'):
|
||||
|
||||
@@ -84,6 +84,7 @@ class CheckinRPCRedeemInputSerializer(serializers.Serializer):
|
||||
type = serializers.ChoiceField(choices=Checkin.CHECKIN_TYPES, default=Checkin.TYPE_ENTRY)
|
||||
ignore_unpaid = serializers.BooleanField(default=False, required=False)
|
||||
questions_supported = serializers.BooleanField(default=True, required=False)
|
||||
use_order_locale = serializers.BooleanField(default=False, required=False)
|
||||
nonce = serializers.CharField(required=False, allow_null=True)
|
||||
datetime = serializers.DateTimeField(required=False, allow_null=True)
|
||||
answers = serializers.JSONField(required=False, allow_null=True)
|
||||
|
||||
@@ -38,11 +38,12 @@ class DiscountSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
|
||||
class Meta:
|
||||
model = Discount
|
||||
fields = ('id', 'active', 'internal_name', 'position', 'all_sales_channels', 'limit_sales_channels',
|
||||
'available_from', 'available_until', 'subevent_mode', 'condition_all_products',
|
||||
'condition_limit_products', 'condition_apply_to_addons', 'condition_min_count', 'condition_min_value',
|
||||
'benefit_discount_matching_percent', 'benefit_only_apply_to_cheapest_n_matches',
|
||||
'benefit_same_products', 'benefit_limit_products', 'benefit_apply_to_addons',
|
||||
'benefit_ignore_voucher_discounted', 'condition_ignore_voucher_discounted')
|
||||
'available_from', 'available_until', 'subevent_mode', 'subevent_date_from', 'subevent_date_until',
|
||||
'condition_all_products', 'condition_limit_products', 'condition_apply_to_addons',
|
||||
'condition_min_count', 'condition_min_value', 'benefit_discount_matching_percent',
|
||||
'benefit_only_apply_to_cheapest_n_matches', 'benefit_same_products', 'benefit_limit_products',
|
||||
'benefit_apply_to_addons', 'benefit_ignore_voucher_discounted',
|
||||
'condition_ignore_voucher_discounted')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -378,6 +378,8 @@ class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
|
||||
if prop.name not in meta_data:
|
||||
current_object.delete()
|
||||
|
||||
instance._prefetched_objects_cache.clear()
|
||||
|
||||
# Item Meta properties
|
||||
if item_meta_properties is not None:
|
||||
current = list(event.item_meta_properties.all())
|
||||
@@ -398,6 +400,8 @@ class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
|
||||
if prop.name not in list(item_meta_properties.keys()):
|
||||
prop.delete()
|
||||
|
||||
instance._prefetched_objects_cache.clear()
|
||||
|
||||
# Seats
|
||||
if seat_category_mapping is not None or ('seating_plan' in validated_data and validated_data['seating_plan'] is None):
|
||||
current_mappings = {
|
||||
|
||||
@@ -607,6 +607,7 @@ class CheckinListOrderPositionSerializer(OrderPositionSerializer):
|
||||
order__status = serializers.SlugRelatedField(read_only=True, slug_field='status', source='order')
|
||||
order__valid_if_pending = serializers.SlugRelatedField(read_only=True, slug_field='valid_if_pending', source='order')
|
||||
order__require_approval = serializers.SlugRelatedField(read_only=True, slug_field='require_approval', source='order')
|
||||
order__locale = serializers.SlugRelatedField(read_only=True, slug_field='locale', source='order')
|
||||
|
||||
class Meta:
|
||||
model = OrderPosition
|
||||
@@ -615,7 +616,7 @@ class CheckinListOrderPositionSerializer(OrderPositionSerializer):
|
||||
'attendee_email', 'voucher', 'tax_rate', 'tax_value', 'secret', 'addon_to', 'subevent', 'checkins',
|
||||
'print_logs', 'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat',
|
||||
'require_attention', 'order__status', 'order__valid_if_pending', 'order__require_approval',
|
||||
'valid_from', 'valid_until', 'blocked')
|
||||
'order__locale', 'valid_from', 'valid_until', 'blocked')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -1518,7 +1519,8 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
self.context['event'],
|
||||
order.sales_channel,
|
||||
[
|
||||
(cp.item_id, cp.subevent_id, cp.price, bool(cp.addon_to), cp.is_bundled, pos._voucher_discount)
|
||||
(cp.item_id, cp.subevent_id, cp.subevent.date_from if cp.subevent_id else None, cp.price,
|
||||
bool(cp.addon_to), cp.is_bundled, pos._voucher_discount)
|
||||
for cp in order_positions
|
||||
]
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ from rest_framework.exceptions import ValidationError
|
||||
|
||||
from pretix.api.serializers.order import (
|
||||
AnswerCreateSerializer, AnswerSerializer, CompatibleCountryField,
|
||||
OrderPositionCreateSerializer,
|
||||
OrderFeeCreateSerializer, OrderPositionCreateSerializer,
|
||||
)
|
||||
from pretix.base.models import ItemVariation, Order, OrderFee, OrderPosition
|
||||
from pretix.base.services.orders import OrderError
|
||||
@@ -104,6 +104,54 @@ class OrderPositionCreateForExistingOrderSerializer(OrderPositionCreateSerialize
|
||||
raise ValidationError(str(e))
|
||||
|
||||
|
||||
class OrderFeeCreateForExistingOrderSerializer(OrderFeeCreateSerializer):
|
||||
order = serializers.SlugRelatedField(slug_field='code', queryset=Order.objects.none(), required=True, allow_null=False)
|
||||
value = serializers.DecimalField(required=True, allow_null=False, decimal_places=2,
|
||||
max_digits=13)
|
||||
internal_type = serializers.CharField(required=False, default="")
|
||||
|
||||
class Meta:
|
||||
model = OrderFee
|
||||
fields = ('order', 'fee_type', 'value', 'description', 'internal_type', 'tax_rule')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not self.context:
|
||||
return
|
||||
self.fields['order'].queryset = self.context['event'].orders.all()
|
||||
self.fields['tax_rule'].queryset = self.context['event'].tax_rules.all()
|
||||
if 'order' in self.context:
|
||||
del self.fields['order']
|
||||
|
||||
def validate(self, data):
|
||||
data = super().validate(data)
|
||||
if 'order' in self.context:
|
||||
data['order'] = self.context['order']
|
||||
return data
|
||||
|
||||
def create(self, validated_data):
|
||||
ocm = self.context['ocm']
|
||||
|
||||
try:
|
||||
f = OrderFee(
|
||||
order=validated_data['order'],
|
||||
fee_type=validated_data['fee_type'],
|
||||
value=validated_data.get('value'),
|
||||
description=validated_data.get('description'),
|
||||
internal_type=validated_data.get('internal_type'),
|
||||
tax_rule=validated_data.get('tax_rule'),
|
||||
)
|
||||
f._calculate_tax()
|
||||
ocm.add_fee(f)
|
||||
if self.context.get('commit', True):
|
||||
ocm.commit()
|
||||
return validated_data['order'].fees.order_by('-pk').first()
|
||||
else:
|
||||
return OrderFee() # fake to appease DRF
|
||||
except OrderError as e:
|
||||
raise ValidationError(str(e))
|
||||
|
||||
|
||||
class OrderPositionInfoPatchSerializer(serializers.ModelSerializer):
|
||||
answers = AnswerSerializer(many=True)
|
||||
country = CompatibleCountryField(source='*')
|
||||
@@ -203,7 +251,7 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = OrderPosition
|
||||
fields = (
|
||||
'item', 'variation', 'subevent', 'seat', 'price', 'tax_rule', 'valid_from', 'valid_until'
|
||||
'item', 'variation', 'subevent', 'seat', 'price', 'tax_rule', 'valid_from', 'valid_until', 'secret'
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -271,6 +319,7 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer):
|
||||
tax_rule = validated_data.get('tax_rule', instance.tax_rule)
|
||||
valid_from = validated_data.get('valid_from', instance.valid_from)
|
||||
valid_until = validated_data.get('valid_until', instance.valid_until)
|
||||
secret = validated_data.get('secret', instance.secret)
|
||||
|
||||
change_item = None
|
||||
if item != instance.item or variation != instance.variation:
|
||||
@@ -303,6 +352,9 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer):
|
||||
if valid_until != instance.valid_until:
|
||||
ocm.change_valid_until(instance, valid_until)
|
||||
|
||||
if secret != instance.secret:
|
||||
ocm.change_ticket_secret(instance, secret)
|
||||
|
||||
if self.context.get('commit', True):
|
||||
ocm.commit()
|
||||
instance.refresh_from_db()
|
||||
@@ -401,6 +453,9 @@ class OrderChangeOperationSerializer(serializers.Serializer):
|
||||
self.fields['split_positions'] = SelectPositionSerializer(
|
||||
many=True, required=False, context=self.context
|
||||
)
|
||||
self.fields['create_fees'] = OrderFeeCreateForExistingOrderSerializer(
|
||||
many=True, required=False, context=self.context
|
||||
)
|
||||
self.fields['patch_fees'] = PatchFeeSerializer(
|
||||
many=True, required=False, context=self.context
|
||||
)
|
||||
|
||||
@@ -165,6 +165,7 @@ class CheckinListViewSet(viewsets.ModelViewSet):
|
||||
|
||||
if not serializer.validated_data.get('position'):
|
||||
kwargs['position'] = OrderPosition.all.filter(
|
||||
order__event=self.request.event,
|
||||
secret=serializer.validated_data['raw_barcode']
|
||||
).first()
|
||||
|
||||
@@ -419,7 +420,7 @@ def _checkin_list_position_queryset(checkinlists, ignore_status=False, ignore_pr
|
||||
|
||||
def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force, checkin_type, ignore_unpaid, nonce,
|
||||
untrusted_input, user, auth, expand, pdf_data, request, questions_supported, canceled_supported,
|
||||
source_type='barcode', legacy_url_support=False, simulate=False, gate=None):
|
||||
source_type='barcode', legacy_url_support=False, simulate=False, gate=None, use_order_locale=False):
|
||||
if not checkinlists:
|
||||
raise ValidationError('No check-in list passed.')
|
||||
|
||||
@@ -693,7 +694,11 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
||||
pass
|
||||
|
||||
# 6. Pass to our actual check-in logic
|
||||
with language(op.order.event.settings.locale):
|
||||
if use_order_locale:
|
||||
locale = op.order.locale
|
||||
else:
|
||||
locale = op.order.event.settings.locale
|
||||
with language(locale):
|
||||
try:
|
||||
perform_checkin(
|
||||
op=op,
|
||||
@@ -908,6 +913,7 @@ class CheckinRPCRedeemView(views.APIView):
|
||||
expand=self.request.query_params.getlist('expand'),
|
||||
pdf_data=self.request.query_params.get('pdf_data', 'false') == 'true',
|
||||
questions_supported=s.validated_data['questions_supported'],
|
||||
use_order_locale=s.validated_data['use_order_locale'],
|
||||
canceled_supported=True,
|
||||
request=self.request, # this is not clean, but we need it in the serializers for URL generation
|
||||
legacy_url_support=False,
|
||||
|
||||
@@ -63,7 +63,8 @@ from pretix.api.serializers.order import (
|
||||
)
|
||||
from pretix.api.serializers.orderchange import (
|
||||
BlockNameSerializer, OrderChangeOperationSerializer,
|
||||
OrderFeeChangeSerializer, OrderPositionChangeSerializer,
|
||||
OrderFeeChangeSerializer, OrderFeeCreateForExistingOrderSerializer,
|
||||
OrderPositionChangeSerializer,
|
||||
OrderPositionCreateForExistingOrderSerializer,
|
||||
OrderPositionInfoPatchSerializer,
|
||||
)
|
||||
@@ -184,7 +185,7 @@ with scopes_disabled():
|
||||
| Q(full_invoice_no__iexact=u)
|
||||
).values_list('order_id', flat=True)
|
||||
|
||||
matching_positions = OrderPosition.objects.filter(
|
||||
matching_positions = OrderPosition.all.filter(
|
||||
Q(order=OuterRef('pk')) & Q(
|
||||
Q(attendee_name_cached__icontains=u) | Q(attendee_email__icontains=u)
|
||||
| Q(secret__istartswith=u)
|
||||
@@ -451,10 +452,9 @@ class EventOrderViewSet(OrderViewSetMixin, viewsets.ModelViewSet):
|
||||
comment = request.data.get('comment', None)
|
||||
cancellation_fee = request.data.get('cancellation_fee', None)
|
||||
if cancellation_fee:
|
||||
try:
|
||||
cancellation_fee = float(Decimal(cancellation_fee))
|
||||
except:
|
||||
cancellation_fee = None
|
||||
cancellation_fee = serializers.DecimalField(max_digits=13, decimal_places=2).to_internal_value(
|
||||
cancellation_fee,
|
||||
)
|
||||
|
||||
order = self.get_object()
|
||||
if not order.cancel_allowed():
|
||||
@@ -988,6 +988,12 @@ class EventOrderViewSet(OrderViewSetMixin, viewsets.ModelViewSet):
|
||||
ocm.cancel_fee(r['fee'])
|
||||
canceled_fees.add(r['fee'])
|
||||
|
||||
for r in serializer.validated_data.get('create_fees', []):
|
||||
pos_serializer = OrderFeeCreateForExistingOrderSerializer(
|
||||
context={'ocm': ocm, 'commit': False, 'event': request.event, **self.get_serializer_context()},
|
||||
)
|
||||
pos_serializer.create(r)
|
||||
|
||||
for r in serializer.validated_data.get('patch_fees', []):
|
||||
if r['fee'] in canceled_fees:
|
||||
continue
|
||||
|
||||
@@ -712,7 +712,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
if name_scheme and len(name_scheme['fields']) > 1:
|
||||
for k, label, w in name_scheme['fields']:
|
||||
row.append(
|
||||
get_name_parts_localized(op.attendee_name_parts, k)
|
||||
get_name_parts_localized(op.attendee_name_parts, k) if op.attendee_name_parts else ''
|
||||
)
|
||||
row += [
|
||||
op.attendee_email,
|
||||
|
||||
@@ -93,7 +93,7 @@ class MarkdownTextarea(forms.Textarea):
|
||||
'<div class="i18n-form-group">%s<div class="i18n-field-markdown-note">%s</div></div>' % (
|
||||
super()._render(template_name, context, renderer=None),
|
||||
_("You can use {markup_name} in this field.").format(
|
||||
markup_name='<a href="https://docs.pretix.eu/en/latest/user/markdown.html" target="_blank">Markdown</a>'
|
||||
markup_name='<a href="https://docs.pretix.eu/guides/markdown/" target="_blank">Markdown</a>'
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -104,7 +104,7 @@ class I18nMarkdownTextarea(i18nfield.forms.I18nTextarea):
|
||||
rendered_widgets = rendered_widgets + [
|
||||
'<div class="i18n-field-markdown-note">%s</div>' % (
|
||||
_("You can use {markup_name} in this field.").format(
|
||||
markup_name='<a href="https://docs.pretix.eu/en/latest/user/markdown.html" target="_blank">Markdown</a>'
|
||||
markup_name='<a href="https://docs.pretix.eu/guides/markdown/" target="_blank">Markdown</a>'
|
||||
)
|
||||
)
|
||||
]
|
||||
@@ -116,7 +116,7 @@ class I18nMarkdownTextInput(i18nfield.forms.I18nTextInput):
|
||||
rendered_widgets = rendered_widgets + [
|
||||
'<div class="i18n-field-markdown-note">%s</div>' % (
|
||||
_("You can use {markup_name} in this field.").format(
|
||||
markup_name='<a href="https://docs.pretix.eu/en/latest/user/markdown.html" target="_blank">Markdown</a>'
|
||||
markup_name='<a href="https://docs.pretix.eu/guides/markdown/" target="_blank">Markdown</a>'
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@@ -58,6 +58,7 @@ from django.urls import reverse
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.text import format_lazy
|
||||
from django.utils.timezone import get_current_timezone
|
||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||
from django_countries import countries
|
||||
@@ -83,8 +84,8 @@ from pretix.base.services.tax import (
|
||||
VATIDFinalError, VATIDTemporaryError, validate_vat_id,
|
||||
)
|
||||
from pretix.base.settings import (
|
||||
COUNTRIES_WITH_STATE_IN_ADDRESS, PERSON_NAME_SALUTATIONS,
|
||||
PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS,
|
||||
COUNTRIES_WITH_STATE_IN_ADDRESS, COUNTRY_STATE_LABEL,
|
||||
PERSON_NAME_SALUTATIONS, PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS,
|
||||
)
|
||||
from pretix.base.templatetags.rich_text import rich_text
|
||||
from pretix.base.timemachine import time_machine_now
|
||||
@@ -127,7 +128,13 @@ class NamePartsWidget(forms.MultiWidget):
|
||||
if fname == 'title' and self.titles:
|
||||
widgets.append(Select(attrs=a, choices=[('', '')] + [(d, d) for d in self.titles[1]]))
|
||||
elif fname == 'salutation':
|
||||
widgets.append(Select(attrs=a, choices=[('', '---'), ('empty', '')] + PERSON_NAME_SALUTATIONS))
|
||||
widgets.append(Select(
|
||||
attrs=a,
|
||||
choices=[
|
||||
('', '---'),
|
||||
('empty', '({})'.format(pgettext_lazy("name_salutation", "not specified"))),
|
||||
] + PERSON_NAME_SALUTATIONS
|
||||
))
|
||||
else:
|
||||
widgets.append(self.widget(attrs=a))
|
||||
super().__init__(widgets, attrs)
|
||||
@@ -245,7 +252,10 @@ class NamePartsFormField(forms.MultiValueField):
|
||||
d.pop('validators', None)
|
||||
field = forms.ChoiceField(
|
||||
**d,
|
||||
choices=[('', '---'), ('empty', '')] + PERSON_NAME_SALUTATIONS
|
||||
choices=[
|
||||
('', '---'),
|
||||
('empty', '({})'.format(pgettext_lazy("name_salutation", "not specified"))),
|
||||
] + PERSON_NAME_SALUTATIONS
|
||||
)
|
||||
else:
|
||||
field = forms.CharField(**defaults)
|
||||
@@ -721,7 +731,7 @@ class BaseQuestionsForm(forms.Form):
|
||||
'data-country-information-url': reverse('js_helpers.states'),
|
||||
}),
|
||||
)
|
||||
c = [('', pgettext_lazy('address', 'Select state'))]
|
||||
c = [('', '---')]
|
||||
fprefix = str(self.prefix) + '-' if self.prefix is not None and self.prefix != '-' else ''
|
||||
cc = None
|
||||
state = None
|
||||
@@ -861,6 +871,23 @@ class BaseQuestionsForm(forms.Form):
|
||||
attrs['data-min'] = q.valid_date_min.isoformat()
|
||||
if q.valid_date_max:
|
||||
attrs['data-max'] = q.valid_date_max.isoformat()
|
||||
if not help_text:
|
||||
if q.valid_date_min and q.valid_date_max:
|
||||
help_text = format_lazy(
|
||||
'Please enter a date between {min} and {max}.',
|
||||
min=date_format(q.valid_date_min, "SHORT_DATE_FORMAT"),
|
||||
max=date_format(q.valid_date_max, "SHORT_DATE_FORMAT"),
|
||||
)
|
||||
elif q.valid_date_min:
|
||||
help_text = format_lazy(
|
||||
'Please enter a date no earlier than {min}.',
|
||||
min=date_format(q.valid_date_min, "SHORT_DATE_FORMAT"),
|
||||
)
|
||||
elif q.valid_date_max:
|
||||
help_text = format_lazy(
|
||||
'Please enter a date no later than {max}.',
|
||||
max=date_format(q.valid_date_max, "SHORT_DATE_FORMAT"),
|
||||
)
|
||||
field = forms.DateField(
|
||||
label=label, required=required,
|
||||
help_text=help_text,
|
||||
@@ -879,6 +906,23 @@ class BaseQuestionsForm(forms.Form):
|
||||
widget=TimePickerWidget(time_format=get_format_without_seconds('TIME_INPUT_FORMATS')),
|
||||
)
|
||||
elif q.type == Question.TYPE_DATETIME:
|
||||
if not help_text:
|
||||
if q.valid_datetime_min and q.valid_datetime_max:
|
||||
help_text = format_lazy(
|
||||
'Please enter a date and time between {min} and {max}.',
|
||||
min=date_format(q.valid_datetime_min, "SHORT_DATETIME_FORMAT"),
|
||||
max=date_format(q.valid_datetime_max, "SHORT_DATETIME_FORMAT"),
|
||||
)
|
||||
elif q.valid_datetime_min:
|
||||
help_text = format_lazy(
|
||||
'Please enter a date and time no earlier than {min}.',
|
||||
min=date_format(q.valid_datetime_min, "SHORT_DATETIME_FORMAT"),
|
||||
)
|
||||
elif q.valid_datetime_max:
|
||||
help_text = format_lazy(
|
||||
'Please enter a date and time no later than {max}.',
|
||||
max=date_format(q.valid_datetime_max, "SHORT_DATETIME_FORMAT"),
|
||||
)
|
||||
field = SplitDateTimeField(
|
||||
label=label, required=required,
|
||||
help_text=help_text,
|
||||
@@ -1079,7 +1123,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
self.fields['country'].choices = CachedCountries()
|
||||
self.fields['country'].widget.attrs['data-country-information-url'] = reverse('js_helpers.states')
|
||||
|
||||
c = [('', pgettext_lazy('address', 'Select state'))]
|
||||
c = [('', '---')]
|
||||
fprefix = self.prefix + '-' if self.prefix else ''
|
||||
cc = None
|
||||
if fprefix + 'country' in self.data:
|
||||
@@ -1088,16 +1132,19 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
cc = str(self.initial['country'])
|
||||
elif self.instance and self.instance.country:
|
||||
cc = str(self.instance.country)
|
||||
state_label = pgettext_lazy('address', 'State')
|
||||
if cc and cc in COUNTRIES_WITH_STATE_IN_ADDRESS:
|
||||
types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[cc]
|
||||
statelist = [s for s in pycountry.subdivisions.get(country_code=cc) if s.type in types]
|
||||
c += sorted([(s.code[3:], s.name) for s in statelist], key=lambda s: s[1])
|
||||
if cc in COUNTRY_STATE_LABEL:
|
||||
state_label = COUNTRY_STATE_LABEL[cc]
|
||||
elif fprefix + 'state' in self.data:
|
||||
self.data = self.data.copy()
|
||||
del self.data[fprefix + 'state']
|
||||
|
||||
self.fields['state'] = forms.ChoiceField(
|
||||
label=pgettext_lazy('address', 'State'),
|
||||
label=state_label,
|
||||
required=False,
|
||||
choices=c,
|
||||
widget=forms.Select(attrs={
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Generated by Django 4.2.16 on 2025-02-28 13:25
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def remove_duplicates(apps, schema_editor):
|
||||
UserKnownLoginSource = apps.get_model("pretixbase", "UserKnownLoginSource")
|
||||
unique_fields = ["user", "agent_type", "device_type", "os_type", "country"]
|
||||
|
||||
duplicates = (
|
||||
UserKnownLoginSource.objects
|
||||
.values(*unique_fields)
|
||||
.order_by()
|
||||
.annotate(latest_id=models.Max('id'), count=models.Count('id'))
|
||||
.filter(count__gt=1)
|
||||
)
|
||||
|
||||
for duplicate in duplicates:
|
||||
(
|
||||
UserKnownLoginSource.objects
|
||||
.filter(**{x: duplicate[x] for x in unique_fields})
|
||||
.exclude(id=duplicate["latest_id"])
|
||||
.delete()
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0277_customerssoclient_require_pkce_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(remove_duplicates, migrations.RunPython.noop),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="userknownloginsource",
|
||||
unique_together={
|
||||
("user", "agent_type", "device_type", "os_type", "country")
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.19 on 2025-03-18 09:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0278_login_source_add_unique_together'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='discount',
|
||||
name='subevent_date_from',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='discount',
|
||||
name='subevent_date_until',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
@@ -602,6 +602,9 @@ class UserKnownLoginSource(models.Model):
|
||||
country = FastCountryField(null=True, blank=True)
|
||||
last_seen = models.DateTimeField()
|
||||
|
||||
class Meta:
|
||||
unique_together = ('user', 'agent_type', 'device_type', 'os_type', 'country')
|
||||
|
||||
|
||||
class StaffSession(models.Model):
|
||||
user = models.ForeignKey('User', on_delete=models.PROTECT)
|
||||
|
||||
@@ -36,7 +36,9 @@ from django_scopes import ScopedManager
|
||||
from pretix.base.decimal import round_decimal
|
||||
from pretix.base.models.base import LoggedModel
|
||||
|
||||
PositionInfo = namedtuple('PositionInfo', ['item_id', 'subevent_id', 'line_price_gross', 'is_addon_to', 'voucher_discount'])
|
||||
PositionInfo = namedtuple('PositionInfo',
|
||||
['item_id', 'subevent_id', 'subevent_date_from', 'line_price_gross', 'is_addon_to',
|
||||
'voucher_discount'])
|
||||
|
||||
|
||||
class Discount(LoggedModel):
|
||||
@@ -171,6 +173,17 @@ class Discount(LoggedModel):
|
||||
"access to sold-out quota will still receive the discount."),
|
||||
)
|
||||
|
||||
subevent_date_from = models.DateTimeField(
|
||||
verbose_name=pgettext_lazy("subevent", "Available for dates starting from"),
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
subevent_date_until = models.DateTimeField(
|
||||
verbose_name=pgettext_lazy("subevent", "Available for dates starting until"),
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
# more feature ideas:
|
||||
# - max_usages_per_order
|
||||
# - promote_to_user_if_almost_satisfied
|
||||
@@ -355,11 +368,15 @@ class Discount(LoggedModel):
|
||||
# First, filter out everything not even covered by our product scope
|
||||
condition_candidates = [
|
||||
idx
|
||||
for idx, (item_id, subevent_id, line_price_gross, is_addon_to, voucher_discount) in positions.items()
|
||||
for idx, (item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, voucher_discount) in
|
||||
positions.items()
|
||||
if (
|
||||
(self.condition_all_products or item_id in limit_products) and
|
||||
(self.condition_apply_to_addons or not is_addon_to) and
|
||||
(not self.condition_ignore_voucher_discounted or voucher_discount is None or voucher_discount == Decimal('0.00'))
|
||||
and (not subevent_id or (
|
||||
self.subevent_date_from is None or subevent_date_from >= self.subevent_date_from)) and (
|
||||
self.subevent_date_until is None or subevent_date_from <= self.subevent_date_until)
|
||||
)
|
||||
]
|
||||
|
||||
@@ -369,7 +386,8 @@ class Discount(LoggedModel):
|
||||
benefit_products = {p.pk for p in self.benefit_limit_products.all()}
|
||||
benefit_candidates = [
|
||||
idx
|
||||
for idx, (item_id, subevent_id, line_price_gross, is_addon_to, voucher_discount) in positions.items()
|
||||
for idx, (item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, voucher_discount) in
|
||||
positions.items()
|
||||
if (
|
||||
item_id in benefit_products and
|
||||
(self.benefit_apply_to_addons or not is_addon_to) and
|
||||
|
||||
@@ -60,6 +60,7 @@ from django.urls import reverse
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import format_html
|
||||
from django.utils.timezone import make_aware, now
|
||||
from django.utils.translation import gettext, gettext_lazy as _
|
||||
from django_scopes import ScopedManager, scopes_disabled
|
||||
@@ -171,7 +172,7 @@ class EventMixin:
|
||||
self.date_to.astimezone(tz), ("D" if short else "l")
|
||||
)
|
||||
|
||||
def get_date_range_display(self, tz=None, force_show_end=False, as_html=False) -> str:
|
||||
def get_date_range_display(self, tz=None, force_show_end=False, as_html=False, try_to_show_times=False) -> str:
|
||||
"""
|
||||
Returns a formatted string containing the start date and the end date
|
||||
of the event with respect to the current locale and to the ``show_date_to``
|
||||
@@ -180,36 +181,48 @@ class EventMixin:
|
||||
tz = tz or self.timezone
|
||||
if (not self.settings.show_date_to and not force_show_end) or not self.date_to:
|
||||
df, dt = self.date_from, self.date_from
|
||||
show_times = try_to_show_times
|
||||
else:
|
||||
df, dt = self.date_from, self.date_to
|
||||
return daterange(df.astimezone(tz), dt.astimezone(tz), as_html)
|
||||
show_times = try_to_show_times and self.settings.show_times and (
|
||||
# Show times if start and end are on the same day ("08:00-10:00")
|
||||
dt.astimezone(tz).date() == df.astimezone(tz).date() or
|
||||
# Show times if start and end are on consecutive days and less than 24h ("23:00-03:00")
|
||||
(dt.astimezone(tz).date() == df.astimezone(tz).date() + timedelta(days=1) and
|
||||
dt.astimezone(tz).time() < df.astimezone(tz).time())
|
||||
)
|
||||
d = daterange(df.astimezone(tz), dt.astimezone(tz), as_html)
|
||||
|
||||
if show_times:
|
||||
if (not self.settings.show_date_to and not force_show_end) or not self.date_to:
|
||||
time_str = _date(self.date_from.astimezone(tz), "TIME_FORMAT")
|
||||
else:
|
||||
time_str = '{}–{}'.format(
|
||||
_date(self.date_from.astimezone(tz), "TIME_FORMAT"),
|
||||
_date(self.date_to.astimezone(tz), "TIME_FORMAT"),
|
||||
)
|
||||
|
||||
if as_html:
|
||||
d = format_html(
|
||||
d + ' <time datetime="{}" data-timezone="{}" data-time-short>{}</time>',
|
||||
self.date_from.isoformat(),
|
||||
str(self.timezone),
|
||||
time_str,
|
||||
)
|
||||
else:
|
||||
d = d + ' ' + time_str
|
||||
|
||||
return d
|
||||
|
||||
def get_date_range_display_with_times(self) -> str: # Helper for usage from templates
|
||||
return self.get_date_range_display(try_to_show_times=True)
|
||||
|
||||
def get_date_range_display_with_times_as_html(self) -> str: # Helper for usage from templates
|
||||
return self.get_date_range_display(try_to_show_times=True, as_html=True)
|
||||
|
||||
def get_date_range_display_as_html(self, tz=None, force_show_end=False) -> str:
|
||||
return self.get_date_range_display(tz, force_show_end, as_html=True)
|
||||
|
||||
def get_time_range_display(self, tz=None, force_show_end=False) -> str:
|
||||
"""
|
||||
Returns a formatted string containing the start time and sometimes the end time
|
||||
of the event with respect to the current locale and to the ``show_date_to``
|
||||
setting. Dates are not shown. This is usually used in combination with get_date_range_display
|
||||
"""
|
||||
tz = tz or self.timezone
|
||||
|
||||
show_date_to = self.date_to and (self.settings.show_date_to or force_show_end) and (
|
||||
# Show date to if start and end are on the same day ("08:00-10:00")
|
||||
self.date_to.astimezone(tz).date() == self.date_from.astimezone(tz).date() or
|
||||
# Show date to if start and end are on consecutive days and less than 24h ("23:00-03:00")
|
||||
(self.date_to.astimezone(tz).date() == self.date_from.astimezone(tz).date() + timedelta(days=1) and
|
||||
self.date_to.astimezone(tz).time() < self.date_from.astimezone(tz).time())
|
||||
# Do not show end time if this is a 5-day event because there's no way to make it understandable
|
||||
)
|
||||
if show_date_to:
|
||||
return '{} – {}'.format(
|
||||
_date(self.date_from.astimezone(tz), "TIME_FORMAT"),
|
||||
_date(self.date_to.astimezone(tz), "TIME_FORMAT"),
|
||||
)
|
||||
return _date(self.date_from.astimezone(tz), "TIME_FORMAT")
|
||||
|
||||
@property
|
||||
def timezone(self):
|
||||
return pytz_deprecation_shim.timezone(self.settings.timezone)
|
||||
|
||||
@@ -821,7 +821,8 @@ class Item(LoggedModel):
|
||||
def ask_attendee_data(self):
|
||||
return self.admission and self.personalized
|
||||
|
||||
def tax(self, price=None, base_price_is='auto', currency=None, invoice_address=None, override_tax_rate=None, include_bundled=False):
|
||||
def tax(self, price=None, base_price_is='auto', currency=None, invoice_address=None, override_tax_rate=None,
|
||||
include_bundled=False, force_fixed_gross_price=False):
|
||||
price = price if price is not None else self.default_price
|
||||
|
||||
bundled_sum = Decimal('0.00')
|
||||
@@ -850,7 +851,7 @@ class Item(LoggedModel):
|
||||
else:
|
||||
t = self.tax_rule.tax(price, base_price_is=base_price_is, invoice_address=invoice_address,
|
||||
override_tax_rate=override_tax_rate, currency=currency or self.event.currency,
|
||||
subtract_from_gross=bundled_sum)
|
||||
subtract_from_gross=bundled_sum, force_fixed_gross_price=force_fixed_gross_price)
|
||||
|
||||
if bundled_sum:
|
||||
t.name = "MIXED!"
|
||||
@@ -1836,7 +1837,7 @@ class Question(LoggedModel):
|
||||
))
|
||||
llen = len(answer.split(','))
|
||||
elif all(isinstance(o, QuestionOption) for o in answer):
|
||||
return o
|
||||
return answer
|
||||
else:
|
||||
l_ = list(self.options.filter(
|
||||
Q(pk__in=[a for a in answer if isinstance(a, int) or a.isdigit()]) |
|
||||
@@ -1915,6 +1916,15 @@ class Question(LoggedModel):
|
||||
if event != item.event:
|
||||
raise ValidationError(_('One or more items do not belong to this event.'))
|
||||
|
||||
def clean(self):
|
||||
if self.valid_date_max and self.valid_date_min and self.valid_date_min > self.valid_date_max:
|
||||
raise ValidationError(_("The maximum date must not be before the minimum value."))
|
||||
if self.valid_datetime_max and self.valid_datetime_min and self.valid_datetime_min > self.valid_datetime_max:
|
||||
raise ValidationError(_("The maximum date must not be before the minimum value."))
|
||||
if self.valid_number_max and self.valid_number_min and self.valid_number_min > self.valid_number_max:
|
||||
raise ValidationError(_("The maximum value must not be lower than the minimum value."))
|
||||
super().clean()
|
||||
|
||||
|
||||
class QuestionOption(models.Model):
|
||||
question = models.ForeignKey('Question', related_name='options', on_delete=models.CASCADE)
|
||||
|
||||
@@ -1199,6 +1199,8 @@ class Order(LockModel, LoggedModel):
|
||||
'invoices': [i.pk for i in invoices] if invoices else [],
|
||||
'attach_tickets': attach_tickets,
|
||||
'attach_ical': attach_ical,
|
||||
'attach_other_files': attach_other_files,
|
||||
'attach_cached_files': [cf.filename for cf in attach_cached_files] if attach_cached_files else [],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2857,6 +2859,8 @@ class OrderPosition(AbstractPosition):
|
||||
'invoices': [i.pk for i in invoices] if invoices else [],
|
||||
'attach_tickets': attach_tickets,
|
||||
'attach_ical': attach_ical,
|
||||
'attach_other_files': attach_other_files,
|
||||
'attach_cached_files': [],
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -286,6 +286,8 @@ class WaitingListEntry(LoggedModel):
|
||||
'subject': subject,
|
||||
'message': email_content,
|
||||
'recipient': recipient,
|
||||
'attach_other_files': attach_other_files,
|
||||
'attach_cached_files': [cf.filename for cf in attach_cached_files] if attach_cached_files else [],
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -330,18 +330,18 @@ class BasePaymentProvider:
|
||||
label=_('Enable payment method'),
|
||||
required=False,
|
||||
)),
|
||||
('_availability_date',
|
||||
RelativeDateField(
|
||||
label=_('Available until'),
|
||||
help_text=_('Users will not be able to choose this payment provider after the given date.'),
|
||||
required=False,
|
||||
)),
|
||||
('_availability_start',
|
||||
RelativeDateField(
|
||||
label=_('Available from'),
|
||||
help_text=_('Users will not be able to choose this payment provider before the given date.'),
|
||||
required=False,
|
||||
)),
|
||||
('_availability_date',
|
||||
RelativeDateField(
|
||||
label=_('Available until'),
|
||||
help_text=_('Users will not be able to choose this payment provider after the given date.'),
|
||||
required=False,
|
||||
)),
|
||||
('_total_min',
|
||||
forms.DecimalField(
|
||||
label=_('Minimum order total'),
|
||||
@@ -1308,6 +1308,9 @@ class OffsettingProvider(BasePaymentProvider):
|
||||
def payment_control_render(self, request: HttpRequest, payment: OrderPayment) -> str:
|
||||
return _('Balanced against orders: %s' % ', '.join(payment.info_data['orders']))
|
||||
|
||||
def refund_control_render(self, request: HttpRequest, payment: OrderPayment) -> str:
|
||||
return self.payment_control_render(request, payment)
|
||||
|
||||
|
||||
class GiftCardPayment(BasePaymentProvider):
|
||||
identifier = "giftcard"
|
||||
@@ -1460,6 +1463,10 @@ class GiftCardPayment(BasePaymentProvider):
|
||||
messages.error(request, _("You cannot pay with gift cards when buying a gift card."))
|
||||
return
|
||||
|
||||
if not request.POST.get("giftcard"):
|
||||
messages.error(request, _("Please enter the code of your gift card."))
|
||||
return
|
||||
|
||||
try:
|
||||
gc = self.event.organizer.accepted_gift_cards.get(
|
||||
secret=request.POST.get("giftcard").strip()
|
||||
|
||||
@@ -1398,7 +1398,8 @@ class CartManager:
|
||||
self.event,
|
||||
self._sales_channel.identifier,
|
||||
[
|
||||
(cp.item_id, cp.subevent_id, cp.line_price_gross, bool(cp.addon_to), cp.is_bundled, cp.listed_price - cp.price_after_voucher)
|
||||
(cp.item_id, cp.subevent_id, cp.subevent.date_from if cp.subevent_id else None, cp.line_price_gross,
|
||||
bool(cp.addon_to), cp.is_bundled, cp.listed_price - cp.price_after_voucher)
|
||||
for cp in positions
|
||||
]
|
||||
)
|
||||
|
||||
@@ -120,7 +120,8 @@ class CrossSellingService:
|
||||
self.event,
|
||||
self.sales_channel,
|
||||
[
|
||||
(cp.item_id, cp.subevent_id, cp.line_price_gross, bool(cp.addon_to), cp.is_bundled,
|
||||
(cp.item_id, cp.subevent_id, cp.subevent.date_from if cp.subevent_id else None, cp.line_price_gross,
|
||||
bool(cp.addon_to), cp.is_bundled,
|
||||
cp.listed_price - cp.price_after_voucher)
|
||||
for cp in self.cartpositions
|
||||
],
|
||||
|
||||
@@ -531,7 +531,7 @@ def send_invoices_to_organizer(sender, **kwargs):
|
||||
if i.event.settings.invoice_email_organizer:
|
||||
with language(i.event.settings.locale):
|
||||
mail(
|
||||
email=i.event.settings.invoice_email_organizer,
|
||||
email=[e.strip() for e in i.event.settings.invoice_email_organizer.split(",")],
|
||||
subject=_('New invoice: {number}').format(number=i.number),
|
||||
template=LazyI18nString.from_gettext(_(
|
||||
'Hello,\n\n'
|
||||
|
||||
+176
-132
@@ -98,8 +98,9 @@ from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.base.services.tasks import ProfiledEventTask, ProfiledTask
|
||||
from pretix.base.signals import (
|
||||
order_approved, order_canceled, order_changed, order_denied, order_expired,
|
||||
order_fee_calculation, order_paid, order_placed, order_reactivated,
|
||||
order_split, order_valid_if_pending, periodic_task, validate_order,
|
||||
order_expiry_changed, order_fee_calculation, order_paid, order_placed,
|
||||
order_reactivated, order_split, order_valid_if_pending, periodic_task,
|
||||
validate_order,
|
||||
)
|
||||
from pretix.base.timemachine import time_machine_now, time_machine_now_assigned
|
||||
from pretix.celery_app import app
|
||||
@@ -302,6 +303,7 @@ def extend_order(order: Order, new_date: datetime, force: bool=False, valid_if_p
|
||||
'state_change': was_expired
|
||||
}
|
||||
)
|
||||
order_expiry_changed.send(sender=order.event, order=order)
|
||||
|
||||
if was_expired:
|
||||
num_invoices = order.invoices.filter(is_cancellation=False).count()
|
||||
@@ -873,7 +875,8 @@ def _check_positions(event: Event, now_dt: datetime, time_machine_now_dt: dateti
|
||||
event,
|
||||
sales_channel.identifier,
|
||||
[
|
||||
(cp.item_id, cp.subevent_id, cp.line_price_gross, bool(cp.addon_to), cp.is_bundled, cp.listed_price - cp.price_after_voucher)
|
||||
(cp.item_id, cp.subevent_id, cp.subevent.date_from if cp.subevent_id else None, cp.line_price_gross,
|
||||
bool(cp.addon_to), cp.is_bundled, cp.listed_price - cp.price_after_voucher)
|
||||
for cp in sorted_positions
|
||||
]
|
||||
)
|
||||
@@ -1561,6 +1564,7 @@ class OrderChangeManager:
|
||||
AddFeeOperation = namedtuple('AddFeeOperation', ('fee', 'price_diff'))
|
||||
CancelFeeOperation = namedtuple('CancelFeeOperation', ('fee', 'price_diff'))
|
||||
RegenerateSecretOperation = namedtuple('RegenerateSecretOperation', ('position',))
|
||||
ChangeSecretOperation = namedtuple('ChangeSecretOperation', ('position', 'new_secret'))
|
||||
ChangeValidFromOperation = namedtuple('ChangeValidFromOperation', ('position', 'valid_from'))
|
||||
ChangeValidUntilOperation = namedtuple('ChangeValidUntilOperation', ('position', 'valid_until'))
|
||||
AddBlockOperation = namedtuple('AddBlockOperation', ('position', 'block_name', 'ignore_from_quota_while_blocked'))
|
||||
@@ -1668,6 +1672,9 @@ class OrderChangeManager:
|
||||
def regenerate_secret(self, position: OrderPosition):
|
||||
self._operations.append(self.RegenerateSecretOperation(position))
|
||||
|
||||
def change_ticket_secret(self, position: OrderPosition, new_secret: str):
|
||||
self._operations.append(self.ChangeSecretOperation(position, new_secret))
|
||||
|
||||
def change_valid_from(self, position: OrderPosition, new_value: datetime):
|
||||
self._operations.append(self.ChangeValidFromOperation(position, new_value))
|
||||
|
||||
@@ -1682,7 +1689,8 @@ class OrderChangeManager:
|
||||
|
||||
def change_price(self, position: OrderPosition, price: Decimal):
|
||||
tax_rule = self._current_tax_rules().get(position.pk, position.tax_rule) or TaxRule.zero()
|
||||
price = tax_rule.tax(price, base_price_is='gross')
|
||||
price = tax_rule.tax(price, base_price_is='gross', invoice_address=self._invoice_address,
|
||||
force_fixed_gross_price=True)
|
||||
|
||||
if position.issued_gift_cards.exists():
|
||||
raise OrderError(self.error_messages['gift_card_change'])
|
||||
@@ -1747,7 +1755,8 @@ class OrderChangeManager:
|
||||
self._operations.append(self.AddFeeOperation(fee, fee.value))
|
||||
|
||||
def change_fee(self, fee: OrderFee, value: Decimal):
|
||||
value = (fee.tax_rule or TaxRule.zero()).tax(value, base_price_is='gross')
|
||||
value = (fee.tax_rule or TaxRule.zero()).tax(value, base_price_is='gross', invoice_address=self._invoice_address,
|
||||
force_fixed_gross_price=True)
|
||||
self._totaldiff += value.gross - fee.value
|
||||
self._invoice_dirty = True
|
||||
self._operations.append(self.FeeValueOperation(fee, value, value.gross - fee.value))
|
||||
@@ -1782,7 +1791,8 @@ class OrderChangeManager:
|
||||
if price is None:
|
||||
price = get_price(item, variation, subevent=subevent, invoice_address=self._invoice_address)
|
||||
elif not isinstance(price, TaxedPrice):
|
||||
price = item.tax(price, base_price_is='gross', invoice_address=self._invoice_address)
|
||||
price = item.tax(price, base_price_is='gross', invoice_address=self._invoice_address,
|
||||
force_fixed_gross_price=True)
|
||||
except TaxRule.SaleNotAllowed:
|
||||
raise OrderError(self.error_messages['tax_rule_country_blocked'])
|
||||
|
||||
@@ -2211,73 +2221,79 @@ class OrderChangeManager:
|
||||
nextposid = self.order.all_positions.aggregate(m=Max('positionid'))['m'] + 1
|
||||
split_positions = []
|
||||
secret_dirty = set()
|
||||
position_cache = {}
|
||||
fee_cache = {}
|
||||
|
||||
for op in self._operations:
|
||||
if isinstance(op, self.ItemOperation):
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
self.order.log_action('pretix.event.order.changed.item', user=self.user, auth=self.auth, data={
|
||||
'position': op.position.pk,
|
||||
'positionid': op.position.positionid,
|
||||
'old_item': op.position.item.pk,
|
||||
'old_variation': op.position.variation.pk if op.position.variation else None,
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
'old_item': position.item.pk,
|
||||
'old_variation': position.variation.pk if position.variation else None,
|
||||
'new_item': op.item.pk,
|
||||
'new_variation': op.variation.pk if op.variation else None,
|
||||
'old_price': op.position.price,
|
||||
'addon_to': op.position.addon_to_id,
|
||||
'new_price': op.position.price
|
||||
'old_price': position.price,
|
||||
'addon_to': position.addon_to_id,
|
||||
'new_price': position.price
|
||||
})
|
||||
op.position.item = op.item
|
||||
op.position.variation = op.variation
|
||||
op.position._calculate_tax()
|
||||
position.item = op.item
|
||||
position.variation = op.variation
|
||||
position._calculate_tax()
|
||||
|
||||
if op.position.voucher_budget_use is not None and op.position.voucher and not op.position.addon_to_id:
|
||||
listed_price = get_listed_price(op.position.item, op.position.variation, op.position.subevent)
|
||||
if not op.position.item.tax_rule or op.position.item.tax_rule.price_includes_tax:
|
||||
price_after_voucher = max(op.position.price, op.position.voucher.calculate_price(listed_price))
|
||||
if position.voucher_budget_use is not None and position.voucher and not position.addon_to_id:
|
||||
listed_price = get_listed_price(position.item, position.variation, position.subevent)
|
||||
if not position.item.tax_rule or position.item.tax_rule.price_includes_tax:
|
||||
price_after_voucher = max(position.price, position.voucher.calculate_price(listed_price))
|
||||
else:
|
||||
price_after_voucher = max(op.position.price - op.position.tax_value, op.position.voucher.calculate_price(listed_price))
|
||||
op.position.voucher_budget_use = max(listed_price - price_after_voucher, Decimal('0.00'))
|
||||
secret_dirty.add(op.position)
|
||||
op.position.save()
|
||||
price_after_voucher = max(position.price - position.tax_value, position.voucher.calculate_price(listed_price))
|
||||
position.voucher_budget_use = max(listed_price - price_after_voucher, Decimal('0.00'))
|
||||
secret_dirty.add(position)
|
||||
position.save()
|
||||
elif isinstance(op, self.MembershipOperation):
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
self.order.log_action('pretix.event.order.changed.membership', user=self.user, auth=self.auth, data={
|
||||
'position': op.position.pk,
|
||||
'positionid': op.position.positionid,
|
||||
'old_membership_id': op.position.used_membership_id,
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
'old_membership_id': position.used_membership_id,
|
||||
'new_membership_id': op.membership.pk if op.membership else None,
|
||||
})
|
||||
op.position.used_membership = op.membership
|
||||
op.position.save()
|
||||
position.used_membership = op.membership
|
||||
position.save()
|
||||
elif isinstance(op, self.SeatOperation):
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
self.order.log_action('pretix.event.order.changed.seat', user=self.user, auth=self.auth, data={
|
||||
'position': op.position.pk,
|
||||
'positionid': op.position.positionid,
|
||||
'old_seat': op.position.seat.name if op.position.seat else "-",
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
'old_seat': position.seat.name if position.seat else "-",
|
||||
'new_seat': op.seat.name if op.seat else "-",
|
||||
'old_seat_id': op.position.seat.pk if op.position.seat else None,
|
||||
'old_seat_id': position.seat.pk if position.seat else None,
|
||||
'new_seat_id': op.seat.pk if op.seat else None,
|
||||
})
|
||||
op.position.seat = op.seat
|
||||
secret_dirty.add(op.position)
|
||||
op.position.save()
|
||||
position.seat = op.seat
|
||||
secret_dirty.add(position)
|
||||
position.save()
|
||||
elif isinstance(op, self.SubeventOperation):
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
self.order.log_action('pretix.event.order.changed.subevent', user=self.user, auth=self.auth, data={
|
||||
'position': op.position.pk,
|
||||
'positionid': op.position.positionid,
|
||||
'old_subevent': op.position.subevent.pk,
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
'old_subevent': position.subevent.pk,
|
||||
'new_subevent': op.subevent.pk,
|
||||
'old_price': op.position.price,
|
||||
'new_price': op.position.price
|
||||
'old_price': position.price,
|
||||
'new_price': position.price
|
||||
})
|
||||
op.position.subevent = op.subevent
|
||||
secret_dirty.add(op.position)
|
||||
if op.position.voucher_budget_use is not None and op.position.voucher and not op.position.addon_to_id:
|
||||
listed_price = get_listed_price(op.position.item, op.position.variation, op.position.subevent)
|
||||
if not op.position.item.tax_rule or op.position.item.tax_rule.price_includes_tax:
|
||||
price_after_voucher = max(op.position.price, op.position.voucher.calculate_price(listed_price))
|
||||
position.subevent = op.subevent
|
||||
secret_dirty.add(position)
|
||||
if position.voucher_budget_use is not None and position.voucher and not position.addon_to_id:
|
||||
listed_price = get_listed_price(position.item, position.variation, position.subevent)
|
||||
if not position.item.tax_rule or position.item.tax_rule.price_includes_tax:
|
||||
price_after_voucher = max(position.price, position.voucher.calculate_price(listed_price))
|
||||
else:
|
||||
price_after_voucher = max(op.position.price - op.position.tax_value, op.position.voucher.calculate_price(listed_price))
|
||||
op.position.voucher_budget_use = max(listed_price - price_after_voucher, Decimal('0.00'))
|
||||
op.position.save()
|
||||
price_after_voucher = max(position.price - position.tax_value, position.voucher.calculate_price(listed_price))
|
||||
position.voucher_budget_use = max(listed_price - price_after_voucher, Decimal('0.00'))
|
||||
position.save()
|
||||
elif isinstance(op, self.AddFeeOperation):
|
||||
self.order.log_action('pretix.event.order.changed.addfee', user=self.user, auth=self.auth, data={
|
||||
'fee': op.fee.pk,
|
||||
@@ -2286,70 +2302,79 @@ class OrderChangeManager:
|
||||
op.fee._calculate_tax()
|
||||
op.fee.save()
|
||||
elif isinstance(op, self.FeeValueOperation):
|
||||
fee = fee_cache.setdefault(op.fee.pk, op.fee)
|
||||
self.order.log_action('pretix.event.order.changed.feevalue', user=self.user, auth=self.auth, data={
|
||||
'fee': op.fee.pk,
|
||||
'old_price': op.fee.value,
|
||||
'fee': fee.pk,
|
||||
'old_price': fee.value,
|
||||
'new_price': op.value.gross
|
||||
})
|
||||
op.fee.value = op.value.gross
|
||||
op.fee._calculate_tax()
|
||||
op.fee.save()
|
||||
fee.value = op.value.gross
|
||||
fee._calculate_tax()
|
||||
fee.save()
|
||||
elif isinstance(op, self.PriceOperation):
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
self.order.log_action('pretix.event.order.changed.price', user=self.user, auth=self.auth, data={
|
||||
'position': op.position.pk,
|
||||
'positionid': op.position.positionid,
|
||||
'old_price': op.position.price,
|
||||
'addon_to': op.position.addon_to_id,
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
'old_price': position.price,
|
||||
'addon_to': position.addon_to_id,
|
||||
'new_price': op.price.gross
|
||||
})
|
||||
op.position.price = op.price.gross
|
||||
op.position.tax_rate = op.price.rate
|
||||
op.position.tax_value = op.price.tax
|
||||
op.position.tax_code = op.price.code
|
||||
op.position.save()
|
||||
position.price = op.price.gross
|
||||
position.tax_rate = op.price.rate
|
||||
position.tax_value = op.price.tax
|
||||
position.tax_code = op.price.code
|
||||
position.save(update_fields=['price', 'tax_rate', 'tax_value', 'tax_code'])
|
||||
elif isinstance(op, self.TaxRuleOperation):
|
||||
if isinstance(op.position, OrderPosition):
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
self.order.log_action('pretix.event.order.changed.tax_rule', user=self.user, auth=self.auth, data={
|
||||
'position': op.position.pk,
|
||||
'positionid': op.position.positionid,
|
||||
'addon_to': op.position.addon_to_id,
|
||||
'old_taxrule': op.position.tax_rule.pk if op.position.tax_rule else None,
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
'addon_to': position.addon_to_id,
|
||||
'old_taxrule': position.tax_rule.pk if position.tax_rule else None,
|
||||
'new_taxrule': op.tax_rule.pk
|
||||
})
|
||||
position._calculate_tax(op.tax_rule)
|
||||
position.save()
|
||||
elif isinstance(op.position, OrderFee):
|
||||
fee = fee_cache.setdefault(op.position.pk, op.position)
|
||||
self.order.log_action('pretix.event.order.changed.tax_rule', user=self.user, auth=self.auth, data={
|
||||
'fee': op.position.pk,
|
||||
'fee_type': op.position.fee_type,
|
||||
'old_taxrule': op.position.tax_rule.pk if op.position.tax_rule else None,
|
||||
'fee': fee.pk,
|
||||
'fee_type': fee.fee_type,
|
||||
'old_taxrule': fee.tax_rule.pk if fee.tax_rule else None,
|
||||
'new_taxrule': op.tax_rule.pk
|
||||
})
|
||||
op.position._calculate_tax(op.tax_rule)
|
||||
op.position.save()
|
||||
fee._calculate_tax(op.tax_rule)
|
||||
fee.save()
|
||||
elif isinstance(op, self.CancelFeeOperation):
|
||||
fee = fee_cache.setdefault(op.fee.pk, op.fee)
|
||||
self.order.log_action('pretix.event.order.changed.cancelfee', user=self.user, auth=self.auth, data={
|
||||
'fee': op.fee.pk,
|
||||
'fee_type': op.fee.fee_type,
|
||||
'old_price': op.fee.value,
|
||||
'fee': fee.pk,
|
||||
'fee_type': fee.fee_type,
|
||||
'old_price': fee.value,
|
||||
})
|
||||
op.fee.canceled = True
|
||||
op.fee.save(update_fields=['canceled'])
|
||||
fee.canceled = True
|
||||
fee.save(update_fields=['canceled'])
|
||||
elif isinstance(op, self.CancelOperation):
|
||||
for gc in op.position.issued_gift_cards.all():
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
for gc in position.issued_gift_cards.all():
|
||||
gc = GiftCard.objects.select_for_update(of=OF_SELF).get(pk=gc.pk)
|
||||
if gc.value < op.position.price:
|
||||
if gc.value < position.price:
|
||||
raise OrderError(_(
|
||||
'A position can not be canceled since the gift card {card} purchased in this order has '
|
||||
'already been redeemed.').format(
|
||||
card=gc.secret
|
||||
))
|
||||
else:
|
||||
gc.transactions.create(value=-op.position.price, order=self.order, acceptor=self.order.event.organizer)
|
||||
gc.transactions.create(value=-position.price, order=self.order, acceptor=self.order.event.organizer)
|
||||
|
||||
for m in op.position.granted_memberships.with_usages().all():
|
||||
for m in position.granted_memberships.with_usages().all():
|
||||
m.canceled = True
|
||||
m.save()
|
||||
|
||||
for opa in op.position.addons.all():
|
||||
for opa in position.addons.all():
|
||||
opa = position_cache.setdefault(opa.pk, opa)
|
||||
for gc in opa.issued_gift_cards.all():
|
||||
gc = GiftCard.objects.select_for_update(of=OF_SELF).get(pk=gc.pk)
|
||||
if gc.value < opa.position.price:
|
||||
@@ -2383,22 +2408,22 @@ class OrderChangeManager:
|
||||
)
|
||||
opa.save(update_fields=['canceled', 'secret'])
|
||||
self.order.log_action('pretix.event.order.changed.cancel', user=self.user, auth=self.auth, data={
|
||||
'position': op.position.pk,
|
||||
'positionid': op.position.positionid,
|
||||
'old_item': op.position.item.pk,
|
||||
'old_variation': op.position.variation.pk if op.position.variation else None,
|
||||
'old_price': op.position.price,
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
'old_item': position.item.pk,
|
||||
'old_variation': position.variation.pk if position.variation else None,
|
||||
'old_price': position.price,
|
||||
'addon_to': None,
|
||||
})
|
||||
op.position.canceled = True
|
||||
if op.position.voucher:
|
||||
Voucher.objects.filter(pk=op.position.voucher.pk).update(redeemed=Greatest(0, F('redeemed') - 1))
|
||||
position.canceled = True
|
||||
if position.voucher:
|
||||
Voucher.objects.filter(pk=position.voucher.pk).update(redeemed=Greatest(0, F('redeemed') - 1))
|
||||
assign_ticket_secret(
|
||||
event=self.event, position=op.position, force_invalidate_if_revokation_list_used=True, force_invalidate=False, save=False
|
||||
event=self.event, position=position, force_invalidate_if_revokation_list_used=True, force_invalidate=False, save=False
|
||||
)
|
||||
if op.position in secret_dirty:
|
||||
secret_dirty.remove(op.position)
|
||||
op.position.save(update_fields=['canceled', 'secret'])
|
||||
if position in secret_dirty:
|
||||
secret_dirty.remove(position)
|
||||
position.save(update_fields=['canceled', 'secret'])
|
||||
elif isinstance(op, self.AddOperation):
|
||||
pos = OrderPosition.objects.create(
|
||||
item=op.item, variation=op.variation, addon_to=op.addon_to,
|
||||
@@ -2423,13 +2448,28 @@ class OrderChangeManager:
|
||||
'valid_until': op.valid_until.isoformat() if op.valid_until else None,
|
||||
})
|
||||
elif isinstance(op, self.SplitOperation):
|
||||
split_positions.append(op.position)
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
split_positions.append(position)
|
||||
elif isinstance(op, self.RegenerateSecretOperation):
|
||||
op.position.web_secret = generate_secret()
|
||||
op.position.save(update_fields=["web_secret"])
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
position.web_secret = generate_secret()
|
||||
position.save(update_fields=["web_secret"])
|
||||
assign_ticket_secret(
|
||||
event=self.event, position=op.position, force_invalidate=True, save=True
|
||||
event=self.event, position=position, force_invalidate=True, save=True
|
||||
)
|
||||
if position in secret_dirty:
|
||||
secret_dirty.remove(position)
|
||||
tickets.invalidate_cache.apply_async(kwargs={'event': self.event.pk,
|
||||
'order': self.order.pk})
|
||||
self.order.log_action('pretix.event.order.changed.secret', user=self.user, auth=self.auth, data={
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
})
|
||||
elif isinstance(op, self.ChangeSecretOperation):
|
||||
if OrderPosition.all.filter(order__event=self.event, secret=op.new_secret).exists():
|
||||
raise OrderError('You cannot assign a position secret that already exists.')
|
||||
op.position.secret = op.new_secret
|
||||
op.position.save(update_fields=["secret"])
|
||||
if op.position in secret_dirty:
|
||||
secret_dirty.remove(op.position)
|
||||
tickets.invalidate_cache.apply_async(kwargs={'event': self.event.pk,
|
||||
@@ -2439,64 +2479,68 @@ class OrderChangeManager:
|
||||
'positionid': op.position.positionid,
|
||||
})
|
||||
elif isinstance(op, self.ChangeValidFromOperation):
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
self.order.log_action('pretix.event.order.changed.valid_from', user=self.user, auth=self.auth, data={
|
||||
'position': op.position.pk,
|
||||
'positionid': op.position.positionid,
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
'new_value': op.valid_from.isoformat() if op.valid_from else None,
|
||||
'old_value': op.position.valid_from.isoformat() if op.position.valid_from else None,
|
||||
'old_value': position.valid_from.isoformat() if position.valid_from else None,
|
||||
})
|
||||
op.position.valid_from = op.valid_from
|
||||
op.position.save(update_fields=['valid_from'])
|
||||
secret_dirty.add(op.position)
|
||||
position.valid_from = op.valid_from
|
||||
position.save(update_fields=['valid_from'])
|
||||
secret_dirty.add(position)
|
||||
elif isinstance(op, self.ChangeValidUntilOperation):
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
self.order.log_action('pretix.event.order.changed.valid_until', user=self.user, auth=self.auth, data={
|
||||
'position': op.position.pk,
|
||||
'positionid': op.position.positionid,
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
'new_value': op.valid_until.isoformat() if op.valid_until else None,
|
||||
'old_value': op.position.valid_until.isoformat() if op.position.valid_until else None,
|
||||
'old_value': position.valid_until.isoformat() if position.valid_until else None,
|
||||
})
|
||||
op.position.valid_until = op.valid_until
|
||||
op.position.save(update_fields=['valid_until'])
|
||||
secret_dirty.add(op.position)
|
||||
position.valid_until = op.valid_until
|
||||
position.save(update_fields=['valid_until'])
|
||||
secret_dirty.add(position)
|
||||
elif isinstance(op, self.AddBlockOperation):
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
self.order.log_action('pretix.event.order.changed.add_block', user=self.user, auth=self.auth, data={
|
||||
'position': op.position.pk,
|
||||
'positionid': op.position.positionid,
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
'block_name': op.block_name,
|
||||
})
|
||||
if op.position.blocked:
|
||||
if op.block_name not in op.position.blocked:
|
||||
op.position.blocked = op.position.blocked + [op.block_name]
|
||||
if position.blocked:
|
||||
if op.block_name not in position.blocked:
|
||||
position.blocked = position.blocked + [op.block_name]
|
||||
else:
|
||||
op.position.blocked = [op.block_name]
|
||||
position.blocked = [op.block_name]
|
||||
if op.ignore_from_quota_while_blocked is not None:
|
||||
op.position.ignore_from_quota_while_blocked = op.ignore_from_quota_while_blocked
|
||||
op.position.save(update_fields=['blocked', 'ignore_from_quota_while_blocked'])
|
||||
if op.position.blocked:
|
||||
op.position.blocked_secrets.update_or_create(
|
||||
position.ignore_from_quota_while_blocked = op.ignore_from_quota_while_blocked
|
||||
position.save(update_fields=['blocked', 'ignore_from_quota_while_blocked'])
|
||||
if position.blocked:
|
||||
position.blocked_secrets.update_or_create(
|
||||
event=self.event,
|
||||
secret=op.position.secret,
|
||||
secret=position.secret,
|
||||
defaults={
|
||||
'blocked': True,
|
||||
'updated': now(),
|
||||
}
|
||||
)
|
||||
elif isinstance(op, self.RemoveBlockOperation):
|
||||
position = position_cache.setdefault(op.position.pk, op.position)
|
||||
self.order.log_action('pretix.event.order.changed.remove_block', user=self.user, auth=self.auth, data={
|
||||
'position': op.position.pk,
|
||||
'positionid': op.position.positionid,
|
||||
'position': position.pk,
|
||||
'positionid': position.positionid,
|
||||
'block_name': op.block_name,
|
||||
})
|
||||
if op.position.blocked and op.block_name in op.position.blocked:
|
||||
op.position.blocked = [b for b in op.position.blocked if b != op.block_name]
|
||||
if not op.position.blocked:
|
||||
op.position.blocked = None
|
||||
if position.blocked and op.block_name in position.blocked:
|
||||
position.blocked = [b for b in position.blocked if b != op.block_name]
|
||||
if not position.blocked:
|
||||
position.blocked = None
|
||||
if op.ignore_from_quota_while_blocked is not None:
|
||||
op.position.ignore_from_quota_while_blocked = op.ignore_from_quota_while_blocked
|
||||
op.position.save(update_fields=['blocked', 'ignore_from_quota_while_blocked'])
|
||||
if not op.position.blocked:
|
||||
position.ignore_from_quota_while_blocked = op.ignore_from_quota_while_blocked
|
||||
position.save(update_fields=['blocked', 'ignore_from_quota_while_blocked'])
|
||||
if not position.blocked:
|
||||
try:
|
||||
bs = op.position.blocked_secrets.get(secret=op.position.secret)
|
||||
bs = position.blocked_secrets.get(secret=position.secret)
|
||||
bs.blocked = False
|
||||
bs.save()
|
||||
except BlockedTicketSecret.DoesNotExist:
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
@@ -162,14 +163,14 @@ def get_line_price(price_after_voucher: Decimal, custom_price_input: Decimal, cu
|
||||
|
||||
|
||||
def apply_discounts(event: Event, sales_channel: Union[str, SalesChannel],
|
||||
positions: List[Tuple[int, Optional[int], Decimal, bool, bool, Decimal]],
|
||||
positions: List[Tuple[int, Optional[int], Optional[datetime], Decimal, bool, bool, Decimal]],
|
||||
collect_potential_discounts: Optional[defaultdict]=None) -> List[Tuple[Decimal, Optional[Discount]]]:
|
||||
"""
|
||||
Applies any dynamic discounts to a cart
|
||||
|
||||
:param event: Event the cart belongs to
|
||||
:param sales_channel: Sales channel the cart was created with
|
||||
:param positions: Tuple of the form ``(item_id, subevent_id, line_price_gross, is_addon_to, is_bundled, voucher_discount)``
|
||||
:param positions: Tuple of the form ``(item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, is_bundled, voucher_discount)``
|
||||
:param collect_potential_discounts: If a `defaultdict(list)` is supplied, all discounts that could be applied to the cart
|
||||
based on the "consumed" items, but lack matching "benefitting" items will be collected therein.
|
||||
The dict will contain a mapping from index in the `positions` list of the item that could be consumed, to a list
|
||||
@@ -191,12 +192,14 @@ def apply_discounts(event: Event, sales_channel: Union[str, SalesChannel],
|
||||
).prefetch_related('condition_limit_products', 'benefit_limit_products').order_by('position', 'pk')
|
||||
for discount in discount_qs:
|
||||
result = discount.apply({
|
||||
idx: PositionInfo(item_id, subevent_id, line_price_gross, is_addon_to, voucher_discount)
|
||||
for idx, (item_id, subevent_id, line_price_gross, is_addon_to, is_bundled, voucher_discount) in enumerate(positions)
|
||||
idx: PositionInfo(item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, voucher_discount)
|
||||
for
|
||||
idx, (item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, is_bundled, voucher_discount)
|
||||
in enumerate(positions)
|
||||
if not is_bundled and idx not in new_prices
|
||||
}, collect_potential_discounts)
|
||||
for k in result.keys():
|
||||
result[k] = (result[k], discount)
|
||||
new_prices.update(result)
|
||||
|
||||
return [new_prices.get(idx, (p[2], None)) for idx, p in enumerate(positions)]
|
||||
return [new_prices.get(idx, (p[3], None)) for idx, p in enumerate(positions)]
|
||||
|
||||
@@ -134,13 +134,13 @@ def order_overview(
|
||||
qs = qs.filter(item__admission=True)
|
||||
items = items.filter(admission=True)
|
||||
|
||||
if date_from and isinstance(date_from, date):
|
||||
if date_from and isinstance(date_from, date) and not isinstance(date_from, datetime):
|
||||
date_from = make_aware(datetime.combine(
|
||||
date_from,
|
||||
time(hour=0, minute=0, second=0, microsecond=0)
|
||||
), event.timezone)
|
||||
|
||||
if date_until and isinstance(date_until, date):
|
||||
if date_until and isinstance(date_until, date) and not isinstance(date_until, datetime):
|
||||
date_until = make_aware(datetime.combine(
|
||||
date_until + timedelta(days=1),
|
||||
time(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
@@ -62,6 +62,9 @@ class VATIDTemporaryError(VATIDError):
|
||||
|
||||
def _validate_vat_id_NO(vat_id, country_code):
|
||||
# Inspired by vat_moss library
|
||||
if not vat_id.startswith("NO"):
|
||||
# prefix is not usually used in Norway, but expected by vat_moss library
|
||||
vat_id = "NO" + vat_id
|
||||
try:
|
||||
vat_id = vat_moss.id.normalize(vat_id)
|
||||
except ValueError:
|
||||
|
||||
@@ -71,6 +71,7 @@ from pretix.base.reldate import (
|
||||
RelativeDateField, RelativeDateTimeField, RelativeDateWrapper,
|
||||
SerializerRelativeDateField, SerializerRelativeDateTimeField,
|
||||
)
|
||||
from pretix.base.validators import multimail_validate
|
||||
from pretix.control.forms import (
|
||||
ExtFileField, FontSelect, MultipleLanguagesWidget, SingleLanguageWidget,
|
||||
)
|
||||
@@ -1233,14 +1234,18 @@ DEFAULTS = {
|
||||
'invoice_email_organizer': {
|
||||
'default': '',
|
||||
'type': str,
|
||||
'form_class': forms.EmailField,
|
||||
'serializer_class': serializers.EmailField,
|
||||
'form_class': forms.CharField,
|
||||
'serializer_class': serializers.CharField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Email address to receive a copy of each invoice"),
|
||||
help_text=_("Each newly created invoice will be sent to this email address shortly after creation. You can "
|
||||
"use this for an automated import of invoices to your accounting system. The invoice will be "
|
||||
"the only attachment of the email."),
|
||||
)
|
||||
validators=[multimail_validate],
|
||||
),
|
||||
'serializer_kwargs': dict(
|
||||
validators=[multimail_validate],
|
||||
),
|
||||
},
|
||||
'show_items_outside_presale_period': {
|
||||
'default': 'True',
|
||||
@@ -2108,7 +2113,7 @@ DEFAULTS = {
|
||||
'form_class': I18nFormField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Event description"),
|
||||
widget=I18nMarkdownTextarea,
|
||||
widget=I18nTextarea,
|
||||
help_text=_(
|
||||
"You can use this to share information with your attendees, such as travel information or the link to a digital event. "
|
||||
"If you keep it empty, we will put a link to the event shop, the admission time, and your organizer name in there. "
|
||||
@@ -2883,7 +2888,8 @@ Your {organizer} team""")) # noqa: W291
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
help_text=_('If you provide a logo image, we will by default not show your event name and date '
|
||||
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
|
||||
'in the page header. If you use a white background, we show your logo with a size of up '
|
||||
'to 1140x120 pixels. Otherwise the maximum size is 1120x120 pixels. You '
|
||||
'can increase the size with the setting below. We recommend not using small details on the picture '
|
||||
'as it will be resized on smaller screens.')
|
||||
),
|
||||
@@ -2926,7 +2932,8 @@ Your {organizer} team""")) # noqa: W291
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
help_text=_('If you provide a logo image, we will by default not show your organization name '
|
||||
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
|
||||
'in the page header. If you use a white background, we show your logo with a size of up '
|
||||
'to 1140x120 pixels. Otherwise the maximum size is 1120x120 pixels. You '
|
||||
'can increase the size with the setting below. We recommend not using small details on the picture '
|
||||
'as it will be resized on smaller screens.')
|
||||
),
|
||||
@@ -2987,7 +2994,7 @@ Your {organizer} team""")) # noqa: W291
|
||||
help_text=_('This picture will be used as a preview if you post links to your ticket shop on social media. '
|
||||
'Facebook advises to use a picture size of 1200 x 630 pixels, however some platforms like '
|
||||
'WhatsApp and Reddit only show a square preview, so we recommend to make sure it still looks good '
|
||||
'only the center square is shown. If you do not fill this, we will use the logo given above.')
|
||||
'if only the center square is shown. If you do not fill this, we will use the logo given above.')
|
||||
),
|
||||
'serializer_class': UploadedFileField,
|
||||
'serializer_kwargs': dict(
|
||||
@@ -3291,6 +3298,8 @@ Your {organizer} team""")) # noqa: W291
|
||||
label=_('Validity of gift card codes in years'),
|
||||
help_text=_('If you set a number here, gift cards will by default expire at the end of the year after this '
|
||||
'many years. If you keep it empty, gift cards do not have an explicit expiry date.'),
|
||||
min_value=0,
|
||||
max_value=99,
|
||||
)
|
||||
},
|
||||
'cookie_consent': {
|
||||
@@ -3703,13 +3712,21 @@ COUNTRIES_WITH_STATE_IN_ADDRESS = {
|
||||
# are actually *used* in postal addresses. This is obviously not complete and opinionated.
|
||||
# Country: [(List of subdivision types as defined by pycountry), (short or long form to be used)]
|
||||
'AU': (['State', 'Territory'], 'short'),
|
||||
'BR': (['State'], 'short'),
|
||||
'BR': (['Federal district', 'State'], 'short'),
|
||||
'CA': (['Province', 'Territory'], 'short'),
|
||||
# 'CN': (['Province', 'Autonomous region', 'Munincipality'], 'long'),
|
||||
'JP': (['Prefecture'], 'long'),
|
||||
'MY': (['State', 'Federal territory'], 'long'),
|
||||
'MX': (['State', 'Federal district'], 'short'),
|
||||
'US': (['State', 'Outlying area', 'District'], 'short'),
|
||||
'IT': (['Province', 'Free municipal consortium', 'Metropolitan city', 'Autonomous province',
|
||||
'Free municipal consortium', 'Decentralized regional entity'], 'short'),
|
||||
}
|
||||
COUNTRY_STATE_LABEL = {
|
||||
# Countries in which the "State" field should not be called "State"
|
||||
'CA': pgettext_lazy('address', 'Province'),
|
||||
'JP': pgettext_lazy('address', 'Prefecture'),
|
||||
'IT': pgettext_lazy('address', 'Province'),
|
||||
}
|
||||
|
||||
settings_hierarkey = Hierarkey(attribute_name='settings')
|
||||
|
||||
@@ -46,7 +46,6 @@ app_cache = {}
|
||||
|
||||
|
||||
def _populate_app_cache():
|
||||
global app_cache
|
||||
apps.check_apps_ready()
|
||||
for ac in apps.app_configs.values():
|
||||
app_cache[ac.name] = ac
|
||||
@@ -563,6 +562,16 @@ as the first argument.
|
||||
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
|
||||
"""
|
||||
|
||||
order_expiry_changed = EventPluginSignal()
|
||||
"""
|
||||
Arguments: ``order``
|
||||
|
||||
This signal is sent out every time an order expiry date is changed as an explicit operation (i.e. not if
|
||||
this is the result of an approval or order change). The order object is given as the first argument.
|
||||
|
||||
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
|
||||
"""
|
||||
|
||||
order_modified = EventPluginSignal()
|
||||
"""
|
||||
Arguments: ``order``
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<code>Host: {{ request.headers.Host }}</code>
|
||||
{% if xfh %}
|
||||
<br>
|
||||
<code>X-Forwarded-For: {{ xfh }}</code>
|
||||
<code>X-Forwarded-Host: {{ xfh }}</code>
|
||||
{% if not settings.USE_X_FORWARDED_HOST %}({% trans "ignored" %}){% endif %}
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="14" viewBox="0 0 18 14"
|
||||
class="{{ cls }}">
|
||||
<path d="M7.713 3.573c-.787-.124-1.677.472-1.511 1.529l.857 3.473c.116.579.578 1.086 1.317 1.086h3.166v3.504c0 1.108 1.556 1.113 1.556.019V8.682c0-.536-.376-1.116-1.099-1.116L9.52 7.563l-.752-2.936c-.147-.648-.583-.981-1.055-1.055v.001Z"></path>
|
||||
<path d="M4.48 5.835a.6.6 0 0 0-.674.725l.71 3.441c.287 1.284 1.39 2.114 2.495 2.114h2.273c.807 0 .811-1.215-.01-1.215H7.188c-.753 0-1.375-.45-1.563-1.289l-.672-3.293c-.062-.3-.26-.452-.474-.483ZM7.433.102a1.468 1.468 0 1 0 0 2.937 1.469 1.469 0 1 0 0-2.937Z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 636 B |
@@ -4,7 +4,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=false">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color: #eee;
|
||||
|
||||
@@ -15,10 +15,7 @@
|
||||
{{ event.name }}
|
||||
<br>
|
||||
{% if event.has_subevents and ev.name|upper != event.name|upper %}{{ ev.name }}<br>{% endif %}
|
||||
{{ ev.get_date_range_display }}
|
||||
{% if event.settings.show_times %}
|
||||
{{ ev.date_from|date:"TIME_FORMAT" }}
|
||||
{% endif %}
|
||||
{{ ev.get_date_range_display_with_times }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -107,10 +104,7 @@
|
||||
{% if groupkey.2.name|upper != event.name|upper %}
|
||||
{{ groupkey.2.name }} ·
|
||||
{% endif %}
|
||||
{{ groupkey.2.get_date_range_display }}
|
||||
{% if event.settings.show_times %}
|
||||
{{ groupkey.2.date_from|date:"TIME_FORMAT" }}
|
||||
{% endif %}
|
||||
{{ groupkey.2.get_date_range_display_with_times }}
|
||||
{% if groupkey.2.location %}
|
||||
<br>
|
||||
{{ groupkey.2.location|oneline }}
|
||||
|
||||
@@ -21,12 +21,15 @@
|
||||
#
|
||||
import pycountry
|
||||
from django.http import JsonResponse
|
||||
from django.utils.translation import pgettext
|
||||
|
||||
from pretix.base.addressvalidation import (
|
||||
COUNTRIES_WITH_STREET_ZIPCODE_AND_CITY_REQUIRED,
|
||||
)
|
||||
from pretix.base.models.tax import VAT_ID_COUNTRIES
|
||||
from pretix.base.settings import COUNTRIES_WITH_STATE_IN_ADDRESS
|
||||
from pretix.base.settings import (
|
||||
COUNTRIES_WITH_STATE_IN_ADDRESS, COUNTRY_STATE_LABEL,
|
||||
)
|
||||
|
||||
|
||||
def states(request):
|
||||
@@ -35,7 +38,11 @@ def states(request):
|
||||
'street': {'required': True},
|
||||
'zipcode': {'required': cc in COUNTRIES_WITH_STREET_ZIPCODE_AND_CITY_REQUIRED},
|
||||
'city': {'required': cc in COUNTRIES_WITH_STREET_ZIPCODE_AND_CITY_REQUIRED},
|
||||
'state': {'visible': cc in COUNTRIES_WITH_STATE_IN_ADDRESS, 'required': cc in COUNTRIES_WITH_STATE_IN_ADDRESS},
|
||||
'state': {
|
||||
'visible': cc in COUNTRIES_WITH_STATE_IN_ADDRESS,
|
||||
'required': cc in COUNTRIES_WITH_STATE_IN_ADDRESS,
|
||||
'label': COUNTRY_STATE_LABEL.get(cc, pgettext('address', 'State')),
|
||||
},
|
||||
'vat_id': {'visible': cc in VAT_ID_COUNTRIES, 'required': False},
|
||||
}
|
||||
if cc not in COUNTRIES_WITH_STATE_IN_ADDRESS:
|
||||
|
||||
@@ -80,4 +80,4 @@ def serve_metrics(request):
|
||||
|
||||
content = "\n".join(output) + "\n"
|
||||
|
||||
return HttpResponse(content)
|
||||
return HttpResponse(content, content_type="text/plain;version=1.0.0;escaping=allow-utf-8")
|
||||
|
||||
@@ -43,7 +43,7 @@ from django.utils.translation import get_language
|
||||
from django_scopes import scope
|
||||
|
||||
from pretix.base.models.auth import StaffSession
|
||||
from pretix.base.settings import GlobalSettingsObject
|
||||
from pretix.base.settings import COUNTRY_STATE_LABEL, GlobalSettingsObject
|
||||
from pretix.control.navigation import (
|
||||
get_event_navigation, get_global_navigation, get_organizer_navigation,
|
||||
)
|
||||
@@ -81,13 +81,13 @@ def _default_context(request):
|
||||
'DEBUG': settings.DEBUG,
|
||||
}
|
||||
_html_head = []
|
||||
if hasattr(request, 'event') and request.user.is_authenticated:
|
||||
if getattr(request, 'event', None) and request.user.is_authenticated:
|
||||
for receiver, response in html_head.send(request.event, request=request):
|
||||
_html_head.append(response)
|
||||
ctx['html_head'] = "".join(_html_head)
|
||||
|
||||
_js_payment_weekdays_disabled = '[]'
|
||||
if getattr(request, 'event', None) and hasattr(request, 'organizer') and request.user.is_authenticated:
|
||||
if getattr(request, 'event', None) and getattr(request, 'organizer', None) and request.user.is_authenticated:
|
||||
ctx['nav_items'] = get_event_navigation(request)
|
||||
|
||||
if request.event.settings.get('payment_term_weekdays'):
|
||||
@@ -140,6 +140,7 @@ def _default_context(request):
|
||||
ctx['js_time_format'] = get_javascript_format('TIME_INPUT_FORMATS')
|
||||
ctx['js_locale'] = get_moment_locale()
|
||||
ctx['select2locale'] = get_language()[:2]
|
||||
ctx['COUNTRY_STATE_LABEL'] = COUNTRY_STATE_LABEL
|
||||
|
||||
ctx['warning_update_available'] = False
|
||||
ctx['warning_update_check_active'] = False
|
||||
|
||||
@@ -45,6 +45,8 @@ class DiscountForm(I18nModelForm):
|
||||
'limit_sales_channels',
|
||||
'available_from',
|
||||
'available_until',
|
||||
'subevent_date_from',
|
||||
'subevent_date_until',
|
||||
'subevent_mode',
|
||||
'condition_all_products',
|
||||
'condition_limit_products',
|
||||
@@ -62,6 +64,8 @@ class DiscountForm(I18nModelForm):
|
||||
field_classes = {
|
||||
'available_from': SplitDateTimeField,
|
||||
'available_until': SplitDateTimeField,
|
||||
'subevent_date_from': SplitDateTimeField,
|
||||
'subevent_date_until': SplitDateTimeField,
|
||||
'condition_limit_products': ItemMultipleChoiceField,
|
||||
'benefit_limit_products': ItemMultipleChoiceField,
|
||||
'limit_sales_channels': SafeModelMultipleChoiceField,
|
||||
@@ -70,6 +74,8 @@ class DiscountForm(I18nModelForm):
|
||||
'subevent_mode': forms.RadioSelect,
|
||||
'available_from': SplitDateTimePickerWidget(),
|
||||
'available_until': SplitDateTimePickerWidget(attrs={'data-date-after': '#id_available_from_0'}),
|
||||
'subevent_date_from': SplitDateTimePickerWidget(),
|
||||
'subevent_date_until': SplitDateTimePickerWidget(attrs={'data-date-after': '#id_subevent_date_from_0'}),
|
||||
'condition_limit_products': forms.CheckboxSelectMultiple(attrs={
|
||||
'data-inverse-dependency': '<[name$=all_products]',
|
||||
'class': 'scrolling-multiple-choice',
|
||||
|
||||
@@ -1475,7 +1475,9 @@ class CountriesAndEUAndStates(CountriesAndEU):
|
||||
def __iter__(self):
|
||||
for country_code, country_name in super().__iter__():
|
||||
yield country_code, country_name
|
||||
if country_code in COUNTRIES_WITH_STATE_IN_ADDRESS:
|
||||
if country_code in COUNTRIES_WITH_STATE_IN_ADDRESS and country_code not in {"IT"}:
|
||||
# Special case for Italy: Provinces are used in addresses, but are too low-level to
|
||||
# have influence on taxes, so we avoid the bloat in the list of selectable countries.
|
||||
types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[country_code]
|
||||
yield from sorted(((state.code, country_name + " - " + state.name)
|
||||
for state in pycountry.subdivisions.get(country_code=country_code)
|
||||
|
||||
@@ -70,6 +70,7 @@ from pretix.helpers.database import (
|
||||
)
|
||||
from pretix.helpers.dicts import move_to_end
|
||||
from pretix.helpers.i18n import get_format_without_seconds, i18ncomp
|
||||
from pretix.helpers.models import flatten_choices
|
||||
|
||||
PAYMENT_PROVIDERS = []
|
||||
|
||||
@@ -177,10 +178,10 @@ class FilterForm(forms.Form):
|
||||
elif isinstance(v, Model):
|
||||
val = '"' + str(v) + '"'
|
||||
elif isinstance(f, forms.MultipleChoiceField):
|
||||
valdict = dict(f.choices)
|
||||
valdict = dict(flatten_choices(f.choices))
|
||||
val = ' or '.join([str(valdict.get(m)) for m in v])
|
||||
elif isinstance(f, forms.ChoiceField):
|
||||
val = str(dict(f.choices).get(v))
|
||||
val = str(dict(flatten_choices(f.choices)).get(v))
|
||||
elif isinstance(v, datetime):
|
||||
val = date_format(v, 'SHORT_DATETIME_FORMAT')
|
||||
elif isinstance(v, Decimal):
|
||||
@@ -196,7 +197,6 @@ class OrderFilterForm(FilterForm):
|
||||
label=_('Search for…'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Search for…'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
@@ -267,9 +267,10 @@ class OrderFilterForm(FilterForm):
|
||||
Q(invoice_no__in=invoice_nos)
|
||||
| Q(full_invoice_no__iexact=u)
|
||||
).values_list('order_id', flat=True)
|
||||
matching_positions = OrderPosition.objects.filter(
|
||||
matching_positions = OrderPosition.all.filter(
|
||||
Q(
|
||||
Q(attendee_name_cached__icontains=u) | Q(attendee_email__icontains=u)
|
||||
| Q(company__icontains=u)
|
||||
| Q(secret__istartswith=u)
|
||||
| Q(pseudonymization_id__istartswith=u)
|
||||
)
|
||||
@@ -849,12 +850,18 @@ class EventOrderExpertFilterForm(EventOrderFilterForm):
|
||||
).distinct()
|
||||
for q in self.event.questions.all():
|
||||
if fdata.get(f'question_{q.pk}'):
|
||||
if q.type == Question.TYPE_BOOLEAN:
|
||||
if q.type in (Question.TYPE_BOOLEAN, Question.TYPE_NUMBER):
|
||||
answers = QuestionAnswer.objects.filter(
|
||||
question_id=q.pk,
|
||||
orderposition__order_id=OuterRef('pk'),
|
||||
answer__exact=fdata.get(f'question_{q.pk}')
|
||||
)
|
||||
elif q.type in (Question.TYPE_DATE, Question.TYPE_TIME, Question.TYPE_DATETIME):
|
||||
answers = QuestionAnswer.objects.filter(
|
||||
question_id=q.pk,
|
||||
orderposition__order_id=OuterRef('pk'),
|
||||
answer__exact=str(fdata.get(f'question_{q.pk}'))
|
||||
)
|
||||
elif q.type in (Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE):
|
||||
answers = QuestionAnswer.objects.filter(
|
||||
question_id=q.pk,
|
||||
@@ -980,7 +987,6 @@ class OrderPaymentSearchFilterForm(forms.Form):
|
||||
label=_('Search for…'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Search for…'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False,
|
||||
)
|
||||
@@ -1242,7 +1248,6 @@ class SubEventFilterForm(FilterForm):
|
||||
label=_('Event name'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Event name'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
@@ -1375,7 +1380,6 @@ class OrganizerFilterForm(FilterForm):
|
||||
label=_('Organizer name'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Organizer name'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
@@ -1433,7 +1437,6 @@ class GiftCardFilterForm(FilterForm):
|
||||
label=_('Search query'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Search query'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
@@ -1485,7 +1488,6 @@ class CustomerFilterForm(FilterForm):
|
||||
label=_('Search query'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Search query'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
@@ -1558,7 +1560,6 @@ class ReusableMediaFilterForm(FilterForm):
|
||||
label=_('Search query'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Search query'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
@@ -1613,7 +1614,6 @@ class TeamFilterForm(FilterForm):
|
||||
label=_('Search query'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Search query'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
@@ -1695,7 +1695,6 @@ class EventFilterForm(FilterForm):
|
||||
label=_('Event name'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Event name'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
@@ -1878,7 +1877,6 @@ class CheckinListAttendeeFilterForm(FilterForm):
|
||||
label=_('Search attendee…'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Search attendee…'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
@@ -2027,7 +2025,6 @@ class UserFilterForm(FilterForm):
|
||||
label=_('Search query'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Search query'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
@@ -2119,7 +2116,6 @@ class VoucherFilterForm(FilterForm):
|
||||
label=_('Search voucher'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Search voucher'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
@@ -2597,7 +2593,6 @@ class DeviceFilterForm(FilterForm):
|
||||
label=_('Search query'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'placeholder': _('Search query'),
|
||||
'autofocus': 'autofocus'
|
||||
}),
|
||||
required=False
|
||||
)
|
||||
|
||||
@@ -25,6 +25,8 @@ import socket
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import RegexValidator
|
||||
from django.utils.text import format_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from pretix.base.forms import SecretKeySettingsField, SettingsForm
|
||||
@@ -54,6 +56,15 @@ class SMTPMailForm(SettingsForm):
|
||||
smtp_password = SecretKeySettingsField(
|
||||
label=_("Password"),
|
||||
required=False,
|
||||
validators=[RegexValidator(
|
||||
r"^[A-Za-z0-9!\"#$%&'()*+,./:;<=>?@\^_`{}|~-]+$",
|
||||
message=format_lazy(
|
||||
_("The password contains characters not supported by our email system. Please only use characters "
|
||||
"A-Z, a-z, 0-9, and common special characters ({characters})."),
|
||||
|
||||
characters=r'!"#$%%&\'()*+,-./:;<=>?@\^_`{}|~'
|
||||
)
|
||||
)]
|
||||
)
|
||||
smtp_use_tls = forms.BooleanField(
|
||||
label=_("Use STARTTLS"),
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
import os.path
|
||||
from datetime import date, datetime, time
|
||||
from decimal import Decimal
|
||||
|
||||
@@ -68,6 +69,7 @@ from pretix.base.services.placeholders import FormPlaceholderMixin
|
||||
from pretix.base.services.pricing import get_price
|
||||
from pretix.control.forms import SplitDateTimeField
|
||||
from pretix.control.forms.widgets import Select2
|
||||
from pretix.helpers.hierarkey import clean_filename
|
||||
from pretix.helpers.money import change_decimal_field
|
||||
|
||||
|
||||
@@ -723,6 +725,9 @@ class OrderMailForm(forms.Form):
|
||||
help_text=_("Will be ignored if tickets exceed a given size limit to ensure email deliverability."),
|
||||
required=False
|
||||
)
|
||||
attach_new_order = forms.BooleanField(
|
||||
required=False
|
||||
)
|
||||
attach_invoices = forms.ModelMultipleChoiceField(
|
||||
label=_("Attach invoices"),
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
@@ -759,6 +764,12 @@ class OrderMailForm(forms.Form):
|
||||
self.fields['attach_invoices'].queryset = order.invoices.all()
|
||||
self._set_field_placeholders('message', ['event', 'order'])
|
||||
self._set_field_placeholders('subject', ['event', 'order'])
|
||||
if order.event.settings.mail_attachment_new_order:
|
||||
self.fields['attach_new_order'].label = _('Attach {file}').format(
|
||||
file=clean_filename(os.path.basename(order.event.settings.mail_attachment_new_order.name))
|
||||
)
|
||||
else:
|
||||
del self.fields['attach_new_order']
|
||||
|
||||
|
||||
class OrderPositionMailForm(OrderMailForm):
|
||||
|
||||
@@ -522,7 +522,8 @@ class OrganizerSettingsForm(SettingsForm):
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
required=False,
|
||||
help_text=_('If you provide a logo image, we will by default not show your organization name '
|
||||
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
|
||||
'in the page header. If you use a white background, we show your logo with a size of up '
|
||||
'to 1140x120 pixels. Otherwise the maximum size is 1120x120 pixels. You '
|
||||
'can increase the size with the setting below. We recommend not using small details on the picture '
|
||||
'as it will be resized on smaller screens.')
|
||||
)
|
||||
|
||||
@@ -43,7 +43,6 @@ from django.dispatch import receiver
|
||||
from django.urls import reverse
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.html import escape, format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
@@ -286,7 +285,7 @@ class OrderChangedSplit(OrderChangeLogEntryType):
|
||||
_('Position #{posid} ({old_item}, {old_price}) split into new order: {order}'),
|
||||
old_item=escape(old_item),
|
||||
posid=data.get('positionid', '?'),
|
||||
order=format_html(mark_safe('<a href="{}">{}</a>'), url, data['new_order']),
|
||||
order=format_html('<a href="{}">{}</a>', url, data['new_order']),
|
||||
old_price=money_filter(Decimal(data['old_price']), event.currency),
|
||||
)
|
||||
|
||||
@@ -303,7 +302,7 @@ class OrderChangedSplitFrom(OrderLogEntryType):
|
||||
})
|
||||
return format_html(
|
||||
_('This order has been created by splitting the order {order}'),
|
||||
order=format_html(mark_safe('<a href="{}">{}</a>'), url, data['original_order']),
|
||||
order=format_html('<a href="{}">{}</a>', url, data['original_order']),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ def get_event_navigation(request: HttpRequest):
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name == 'event.settings.payment',
|
||||
'active': url.url_name in ('event.settings.payment', 'event.settings.payment.provider'),
|
||||
},
|
||||
{
|
||||
'label': _('Plugins'),
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
{% endfor %}
|
||||
<p>
|
||||
{% blocktrans trimmed count count=cnt %}
|
||||
Are you sure you want to permanently delete the check-ins of <strong>one ticket</strong>.
|
||||
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 %}
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
<td>{{ e.item }}{% if e.variation %} – {{ e.variation }}{% endif %}</td>
|
||||
{% if request.event.has_subevents and not checkinlist.subevent %}
|
||||
<td>
|
||||
{{ e.subevent.name }} – {{ e.subevent.get_date_range_display }} {{ e.subevent.date_from|date:"TIME_FORMAT" }}
|
||||
{{ e.subevent.name }} – {{ e.subevent.get_date_range_display_with_times }}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if seats %}
|
||||
|
||||
@@ -127,8 +127,7 @@
|
||||
{% if request.event.has_subevents %}
|
||||
{% if cl.subevent %}
|
||||
<td>
|
||||
{{ cl.subevent.name }} – {{ cl.subevent.get_date_range_display }}
|
||||
{{ cl.subevent.date_from|date:"TIME_FORMAT" }}
|
||||
{{ cl.subevent.name }} – {{ cl.subevent.get_date_range_display_with_times }}
|
||||
</td>
|
||||
{% else %}
|
||||
<td>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<p>{{ plugin.description|safe }}</p>
|
||||
<p class="plugin-description">{{ plugin.description|safe }}</p>
|
||||
{% if plugin.restricted and plugin.module not in request.event.settings.allowed_restricted_plugins %}
|
||||
<p class="text-muted">
|
||||
<span class="fa fa-info-circle" aria-hidden="true"></span>
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="delete" value="yes" />
|
||||
<b>{% trans "Permanently delete all orders created in test mode" %}</b>
|
||||
<strong>{% trans "Permanently delete all orders created in test mode" %}</strong>
|
||||
</label>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
|
||||
@@ -48,19 +48,19 @@
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<td colspan="4">
|
||||
<br>
|
||||
{% url "control:event.settings.plugins" event=request.event.slug organizer=request.organizer.slug as plugin_settings_url %}
|
||||
{% blocktrans trimmed with plugin_settings_href='href="'|add:plugin_settings_url|add:'"'|safe %}
|
||||
There are no payment providers available. Please go to the
|
||||
<a {{ plugin_settings_href }}>plugin settings</a> and activate one or more payment plugins.
|
||||
{% endblocktrans %}
|
||||
<a href="{{ plugin_settings_url }}#tab-0-1-open" class="btn btn-default">
|
||||
<i class="fa fa-plus"></i> {% trans "Enable additional payment plugins" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Deadlines" %}</legend>
|
||||
|
||||
@@ -10,20 +10,40 @@
|
||||
software functionality, connect your event to third-party services, or apply other forms of customizations.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% if "success" in request.GET %}
|
||||
<div class="alert alert-success">
|
||||
{% trans "Your changes have been saved." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col-lg-10">
|
||||
<p><input type="search" id="plugin_search_input" class="form-control" placeholder="{% trans "Search" %}"></p>
|
||||
</div>
|
||||
<div class="col-lg-2 text-right">
|
||||
<p class="btn-group btn-group-flex" data-toggle="buttons">
|
||||
<label class="btn btn-primary-if-active active"><input type="radio" name="plugin_state_filter" value="all" checked> {% trans "All" %}</label>
|
||||
<label class="btn btn-primary-if-active"><input type="radio" name="plugin_state_filter" value="active"> {% trans "Active" %}</label>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<form action="" method="post" class="form-horizontal form-plugins">
|
||||
{% csrf_token %}
|
||||
{% if "success" in request.GET %}
|
||||
<div class="alert alert-success">
|
||||
{% trans "Your changes have been saved." %}
|
||||
<div id="plugin_search_results" class="panel panel-default collapse">
|
||||
<div class="panel-heading">
|
||||
<button type="button" class="close" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
{% trans "Search results" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="tabbed-form">
|
||||
<div class="panel-body">
|
||||
<div class="plugin-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="plugin_tabs"><div class="tabbed-form">
|
||||
{% for cat, catlabel, plist, has_pictures in plugins %}
|
||||
<fieldset>
|
||||
<fieldset data-plugin-category="{{ cat }}" data-plugin-category-label="{{ catlabel }}">
|
||||
<legend>{{ catlabel }}</legend>
|
||||
<div class="plugin-list">
|
||||
{% for plugin in plist %}
|
||||
<div class="plugin-container {% if plugin.featured %}featured-plugin{% endif %}" id="plugin_{{ plugin.module }}">
|
||||
{% for plugin, is_active, settings_links, navigation_links in plist %}
|
||||
<div class="plugin-container {% if plugin.featured %}featured-plugin{% endif %}" id="plugin_{{ plugin.module }}" data-plugin-module="{{ plugin.module }}" data-plugin-name="{{ plugin.name }}">
|
||||
{% if plugin.featured %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
@@ -49,8 +69,8 @@
|
||||
{% if show_meta %}
|
||||
<span class="text-muted text-sm">{{ plugin.version }}</span>
|
||||
{% endif %}
|
||||
{% if plugin.module in plugins_active %}
|
||||
<span class="label label-success">
|
||||
{% if is_active %}
|
||||
<span class="label label-success" data-is-active>
|
||||
<span class="fa fa-check" aria-hidden="true"></span>
|
||||
{% trans "Active" %}
|
||||
</span>
|
||||
@@ -66,8 +86,32 @@
|
||||
<div class="plugin-action">
|
||||
<span class="text-muted">{% trans "Not available" %}</span>
|
||||
</div>
|
||||
{% elif plugin.module in plugins_active %}
|
||||
{% elif is_active %}
|
||||
<div class="plugin-action flip">
|
||||
{% if navigation_links %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle{% if plugin.featured %} btn-lg{% endif %}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="{% trans "Open plugin settings" %}">
|
||||
<span class="fa fa-compass"></span> {% trans "Go to" %} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{% for link in navigation_links %}
|
||||
<li><a href="{{ link.0 }}">{{ link.1 }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if settings_links %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle{% if plugin.featured %} btn-lg{% endif %}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="{% trans "Open plugin settings" %}">
|
||||
<span class="fa fa-cog"></span> {% trans "Settings" %} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{% for link in settings_links %}
|
||||
<li><a href="{{ link.0 }}">{{ link.1 }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<button class="btn btn-default{% if plugin.featured %} btn-lg{% endif %}" name="plugin:{{ plugin.module }}"
|
||||
value="disable">{% trans "Disable" %}</button>
|
||||
</div>
|
||||
@@ -86,6 +130,7 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div></div>
|
||||
</form>
|
||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/plugins.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -110,23 +110,26 @@
|
||||
{% if not hide_orga %}<td>{{ e.organizer }}</td>{% endif %}
|
||||
<td class="event-date-col">
|
||||
{% if e.has_subevents %}
|
||||
{{ e.min_from|default_if_none:""|date:"SHORT_DATETIME_FORMAT" }}
|
||||
<span class="fa fa-fw- fa-calendar"></span>
|
||||
{% trans "Event series" %}
|
||||
<br>
|
||||
<span class="text-muted">
|
||||
{% if e.min_from %}
|
||||
{{ e.min_from|date:"SHORT_DATETIME_FORMAT" }} –<br>
|
||||
{{ e.max_fromto|default_if_none:e.max_to|default_if_none:e.max_from|date:"SHORT_DATETIME_FORMAT" }}
|
||||
{% else %}
|
||||
{% trans "No dates" context "subevent" %}
|
||||
{% endif %}
|
||||
</span>
|
||||
{% else %}
|
||||
{{ e.get_short_date_from_display }}
|
||||
{% endif %}
|
||||
{% if e.has_subevents %}
|
||||
<span class="label label-default">{% trans "Series" %}</span>
|
||||
{% endif %}
|
||||
{% if e.settings.show_date_to and e.date_to %}
|
||||
–<br>
|
||||
{% if e.has_subevents %}
|
||||
{{ e.max_fromto|default_if_none:e.max_from|default_if_none:e.max_to|default_if_none:""|date:"SHORT_DATETIME_FORMAT" }}
|
||||
{% else %}
|
||||
{% if e.settings.show_date_to and e.date_to %}
|
||||
–<br>
|
||||
{{ e.get_short_date_to_display }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if e.settings.timezone != request.timezone %}
|
||||
<span class="fa fa-globe text-muted" data-toggle="tooltip" title="{{ e.timezone }}"></span>
|
||||
<span class="fa fa-globe text-muted" data-toggle="tooltip" title="{{ e.tzname }}"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -40,6 +40,10 @@
|
||||
<tr data-dnd-id="{{ c.id }}">
|
||||
<td>
|
||||
<strong><a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}">{{ c.internal_name|default:c.name }}</a></strong>
|
||||
<br>
|
||||
<small class="text-muted">
|
||||
#{{ c.pk }}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
{{ c.get_category_type_display }}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
{% bootstrap_field form.condition_ignore_voucher_discounted layout="control" %}
|
||||
{% if form.subevent_mode %}
|
||||
{% bootstrap_field form.subevent_mode layout="control" %}
|
||||
{% bootstrap_field form.subevent_date_from layout="control" %}
|
||||
{% bootstrap_field form.subevent_date_until layout="control" %}
|
||||
{% endif %}
|
||||
<div class="form-group form-alternatives">
|
||||
<label class="col-md-3 control-label">
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
<th class="iconcol"></th>
|
||||
<th class="iconcol"></th>
|
||||
<th class="iconcol"></th>
|
||||
<th class="iconcol"></th>
|
||||
<th class="text-right flip">{% trans "Default price" %}</th>
|
||||
<th class="action-col-2"><span class="sr-only">Edit</span></th>
|
||||
</tr>
|
||||
@@ -111,6 +112,14 @@
|
||||
<span class="fa fa-bars fa-fw text-muted" data-toggle="tooltip" title="{% trans "Product with variations" %}"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if i.requires_seat %}
|
||||
<span data-toggle="tooltip"
|
||||
title="{% if request.event.has_subevents %}{% trans "Product assigned to seating plan for one or more dates" context "subevent" %}{% else %}{% trans "Product assigned to seating plan" %}{% endif %}">
|
||||
{% include "icons/seat.svg" with cls="svg-icon text-muted" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if i.category.is_addon %}
|
||||
<span class="fa fa-plus-square fa-fw text-muted" data-toggle="tooltip"
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
</td>
|
||||
{% if request.event.has_subevents %}
|
||||
<td>
|
||||
{{ q.subevent.name }} – {{ q.subevent.get_date_range_display }} {{ q.subevent.date_from|date:"TIME_FORMAT" }}
|
||||
{{ q.subevent.name }} – {{ q.subevent.get_date_range_display_with_times }}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>{% if q.size == None %}Unlimited{% else %}{{ q.size }}{% endif %}</td>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
{% load eventsignal %}
|
||||
{% load l10n %}
|
||||
{% load phone_format %}
|
||||
{% load getitem %}
|
||||
{% block title %}
|
||||
{% blocktrans trimmed with code=order.code %}
|
||||
Order details: {{ code }}
|
||||
@@ -397,7 +398,7 @@
|
||||
{% elif c.auto_checked_in %}
|
||||
<span class="fa fa-fw fa-magic text-success" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Automatically checked in: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
{% else %}
|
||||
<span class="fa fa-fw fa-check {% if c.list.consider_tickets_used %}text-success{% else %}text-muted{% endif %}" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Entry scan: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
<span class="fa fa-fw {% if c.list.consider_tickets_used %}text-success fa-check{% else %}text-muted fa-check-circle-o{% endif %}" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Entry scan: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -412,10 +413,7 @@
|
||||
{% endif %}
|
||||
{% if line.seat %}
|
||||
<br />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="14" viewBox="0 0 4.7624999 3.7041668" class="svg-icon">
|
||||
<path
|
||||
d="m 1.9592032,1.8522629e-4 c -0.21468,0 -0.38861,0.17394000371 -0.38861,0.38861000371 0,0.21466 0.17393,0.38861 0.38861,0.38861 0.21468,0 0.3886001,-0.17395 0.3886001,-0.38861 0,-0.21467 -0.1739201,-0.38861000371 -0.3886001,-0.38861000371 z m 0.1049,0.84543000371 c -0.20823,-0.0326 -0.44367,0.12499 -0.39998,0.40462997 l 0.20361,1.01854 c 0.0306,0.15316 0.15301,0.28732 0.3483,0.28732 h 0.8376701 v 0.92708 c 0,0.29313 0.41187,0.29447 0.41187,0.005 v -1.19115 c 0,-0.14168 -0.0995,-0.29507 -0.29094,-0.29507 l -0.65578,-10e-4 -0.1757,-0.87644 C 2.3042533,0.95300523 2.1890432,0.86500523 2.0641032,0.84547523 Z m -0.58549,0.44906997 c -0.0946,-0.0134 -0.20202,0.0625 -0.17829,0.19172 l 0.18759,0.91054 c 0.0763,0.33956 0.36802,0.55914 0.66042,0.55914 h 0.6015201 c 0.21356,0 0.21448,-0.32143 -0.003,-0.32143 H 2.1954632 c -0.19911,0 -0.36364,-0.11898 -0.41341,-0.34107 l -0.17777,-0.87126 c -0.0165,-0.0794 -0.0688,-0.11963 -0.12557,-0.12764 z"/>
|
||||
</svg>
|
||||
{% include "icons/seat.svg" with cls="svg-icon" %}
|
||||
{{ line.seat }}
|
||||
{% endif %}
|
||||
{% if line.voucher %}
|
||||
@@ -428,11 +426,7 @@
|
||||
{% endif %}
|
||||
{% if line.subevent %}
|
||||
<br/>
|
||||
<span class="fa fa-calendar fa-fw"></span> {{ line.subevent.name }} · {{ line.subevent.get_date_range_display }}
|
||||
{% if event.settings.show_times %}
|
||||
<span class="fa fa-clock-o"></span>
|
||||
{{ line.subevent.date_from|date:"TIME_FORMAT" }}
|
||||
{% endif %}
|
||||
<span class="fa fa-calendar fa-fw"></span> {{ line.subevent.name }} · {{ line.subevent.get_date_range_display_with_times }}
|
||||
{% endif %}
|
||||
{% if line.used_membership %}
|
||||
<br /><span class="fa fa-id-card fa-fw" aria-hidden="true"></span>
|
||||
@@ -560,8 +554,8 @@
|
||||
{% if line.street or line.zipcode or line.city or line.country %}
|
||||
{{ line.street|default_if_none:""|linebreaksbr }}<br>
|
||||
{{ line.zipcode|default_if_none:"" }} {{ line.city|default_if_none:"" }}<br>
|
||||
{% if line.state %}{{ line.state_for_address }}<br>{% endif %}
|
||||
{{ line.country.name|default_if_none:"" }}
|
||||
{% if line.state %}<br>{{ line.state }}{% endif %}
|
||||
{% else %}
|
||||
<em>{% trans "not answered" %}</em>
|
||||
{% endif %}
|
||||
@@ -959,7 +953,7 @@
|
||||
<dt>{% trans "Country" %}</dt>
|
||||
<dd>{{ order.invoice_address.country.name|default:order.invoice_address.country_old }}</dd>
|
||||
{% if order.invoice_address.state %}
|
||||
<dt>{% trans "State" context "address" %}</dt>
|
||||
<dt>{% trans "State" context "address" as state_label %}{{ COUNTRY_STATE_LABEL|getitem:order.invoice_address.country.code|default:state_label }}</dt>
|
||||
<dd>{{ order.invoice_address.state_name }}</dd>
|
||||
{% endif %}
|
||||
{% if request.event.settings.invoice_address_vatid %}
|
||||
|
||||
@@ -51,6 +51,44 @@
|
||||
{{ log.parsed_data.subject }}</strong>
|
||||
</p>
|
||||
<pre>{{ log.parsed_data.message }}</pre>
|
||||
<ul class="list-unstyled">
|
||||
{% comment %}
|
||||
{# Unfortunately, we do not have reliable info whether tickets were attached. #}
|
||||
{% if log.parsed_data.attach_tickets %}
|
||||
<li><span class="fa fa-files-o fa-fw"></span> {% trans "Tickets" %}</li>
|
||||
{% endif %}
|
||||
{% endcomment %}
|
||||
{% if log.parsed_data.attach_ical %}
|
||||
<li><span class="fa fa-calendar-o fa-fw"></span> {% trans "Calendar invite" %}</li>
|
||||
{% endif %}
|
||||
{% if log.parsed_data.invoices %}
|
||||
{% for i in log.parsed_invoices %}
|
||||
<li>
|
||||
<span class="fa fa-file-o fa-fw"></span>
|
||||
<a href="{% url "control:event.invoice.download" invoice=i.pk event=request.event.slug organizer=request.event.organizer.slug %}" target="_blank">
|
||||
{% if i.is_cancellation %}{% trans "Cancellation" context "invoice" %}{% else %}{% trans "Invoice" %}{% endif %}
|
||||
{{ i.number }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if log.parsed_data.attach_other_files %}
|
||||
{% for f in log.parsed_other_files %}
|
||||
<li>
|
||||
<span class="fa fa-file-o fa-fw"></span>
|
||||
{{ f }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if log.parsed_data.attach_cached_files %}
|
||||
{% for f in log.parsed_data.attach_cached_files %}
|
||||
<li>
|
||||
<span class="fa fa-file-o fa-fw"></span>
|
||||
{{ f }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
{% bootstrap_field form.subject layout='horizontal' %}
|
||||
{% bootstrap_field form.message layout='horizontal' %}
|
||||
{% bootstrap_field form.attach_tickets layout='horizontal' %}
|
||||
{% if form.attach_new_order %}
|
||||
{% bootstrap_field form.attach_new_order layout='horizontal' %}
|
||||
{% endif %}
|
||||
{% if form.attach_invoices %}
|
||||
{% bootstrap_field form.attach_invoices layout='horizontal' %}
|
||||
{% endif %}
|
||||
|
||||
@@ -44,8 +44,7 @@
|
||||
<form class="form-inline"
|
||||
action="{% url "control:event.orders.go" event=request.event.slug organizer=request.event.organizer.slug %}">
|
||||
<p class="input-group">
|
||||
<input type="text" name="code" class="form-control" placeholder="{% trans "Order code" %}"
|
||||
autofocus>
|
||||
<input type="text" name="code" class="form-control" placeholder="{% trans "Order code" %}">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="submit">{% trans "Go!" %}</button>
|
||||
</span>
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
{% block title %}{% trans "Order search" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h1>{% trans "Order search" %}</h1>
|
||||
<form class="form-horizontal" action="{% url "control:event.orders" event=request.event.slug organizer=request.event.organizer.slug %}" method="get">
|
||||
<form class="form-horizontal" method="post">
|
||||
{% csrf_token %}
|
||||
{% for f in forms %}
|
||||
{% bootstrap_form_errors f layout='control' %}
|
||||
{% for field in f %}
|
||||
|
||||
@@ -86,23 +86,26 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if e.has_subevents %}
|
||||
{{ e.min_from|default_if_none:""|date:"SHORT_DATETIME_FORMAT" }}
|
||||
<span class="fa fa-fw- fa-calendar"></span>
|
||||
{% trans "Event series" %}
|
||||
<br>
|
||||
<span class="text-muted">
|
||||
{% if e.min_from %}
|
||||
{{ e.min_from|date:"SHORT_DATETIME_FORMAT" }} –<br>
|
||||
{{ e.max_fromto|default_if_none:e.max_to|default_if_none:e.max_from|date:"SHORT_DATETIME_FORMAT" }}
|
||||
{% else %}
|
||||
{% trans "No dates" context "subevent" %}
|
||||
{% endif %}
|
||||
</span>
|
||||
{% else %}
|
||||
{{ e.get_short_date_from_display }}
|
||||
{% endif %}
|
||||
{% if e.has_subevents %}
|
||||
<span class="label label-default">{% trans "Series" %}</span>
|
||||
{% endif %}
|
||||
{% if e.settings.show_date_to and e.date_to %}
|
||||
–<br>
|
||||
{% if e.has_subevents %}
|
||||
{{ e.max_fromto|default_if_none:e.max_from|default_if_none:e.max_to|default_if_none:""|date:"SHORT_DATETIME_FORMAT" }}
|
||||
{% else %}
|
||||
{% if e.settings.show_date_to and e.date_to %}
|
||||
–<br>
|
||||
{{ e.get_short_date_to_display }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if e.settings.timezone != request.timezone %}
|
||||
<span class="fa fa-globe text-muted" data-toggle="tooltip" title="{{ e.timezone }}"></span>
|
||||
<span class="fa fa-globe text-muted" data-toggle="tooltip" title="{{ e.tzname }}"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -182,8 +182,54 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row control-group pdf-info">
|
||||
<div class="col-sm-12">
|
||||
<label for="pdf-info-locale">{% trans "Preferred language" %}</label><br>
|
||||
<select class="form-control" id="pdf-info-locale">
|
||||
<option value="">{% trans "Order locale" %}</option>
|
||||
{% for l in locales %}
|
||||
<option value="{{ l.0 }}">{{ l.1 }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row control-group pdf-info">
|
||||
<hr/>
|
||||
<div class="col-sm-12">
|
||||
<label>{% trans "Upload PDF as background" %}</label>
|
||||
<p class="text-muted">
|
||||
{% blocktrans trimmed %}
|
||||
You can upload a PDF to use as a custom background.
|
||||
The paper size will match the PDF.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p>
|
||||
<span class="btn btn-default fileinput-button background-button btn-block">
|
||||
<i class="fa fa-upload"></i>
|
||||
<span>{% trans "Upload PDF as background" %}</span>
|
||||
<input id="fileupload" type="file" name="background" accept="application/pdf">
|
||||
</span>
|
||||
</p>
|
||||
<p class="text-center">
|
||||
<a class="btn btn-link background-download-button" href="{{ pdf }}" target="_blank">
|
||||
<i class="fa fa-download"></i>
|
||||
<span class="small">{% trans "Download current background" %}</span>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group pdf-info">
|
||||
<div class="col-sm-12">
|
||||
<label>{% trans "Or choose custom paper size" %}</label>
|
||||
<p class="text-muted">
|
||||
{% blocktrans trimmed %}
|
||||
To manually change the paper size, you need to create a new, empty background.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group pdf-info">
|
||||
<div class="col-sm-6">
|
||||
<label for="pdf-info-width">{% trans "Width (mm)" %}</label><br>
|
||||
<input type="number" id="pdf-info-width" class="input-block-level form-control">
|
||||
@@ -195,45 +241,12 @@
|
||||
</div>
|
||||
<div class="row control-group pdf-info">
|
||||
<div class="col-sm-12">
|
||||
<label>{% trans "Background PDF" %}</label><br>
|
||||
<p>
|
||||
<button class="btn btn-default background-button" id="pdf-empty">
|
||||
<button class="btn btn-default background-button btn-block" id="pdf-empty">
|
||||
<i class="fa fa-file-o"></i>
|
||||
{% trans "Create empty background" %}
|
||||
</button>
|
||||
</p>
|
||||
<span class="btn btn-default fileinput-button background-button">
|
||||
<i class="fa fa-upload"></i>
|
||||
<span>{% trans "Upload custom background" %}</span>
|
||||
<input id="fileupload" type="file" name="background" accept="application/pdf">
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-12 help-inline">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
After you changed the page size, you need to create a new empty background. If you
|
||||
want to use a custom background, it already needs to have the correct size.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<p>
|
||||
<a class="btn btn-default background-download-button" href="{{ pdf }}" target="_blank">
|
||||
<i class="fa fa-download"></i>
|
||||
<span>{% trans "Download current background" %}</span>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group pdf-info">
|
||||
<hr/>
|
||||
<div class="col-sm-12">
|
||||
<label for="pdf-info-locale">{% trans "Preferred language" %}</label><br>
|
||||
<select class="form-control" id="pdf-info-locale">
|
||||
<option value="">{% trans "Order locale" %}</option>
|
||||
{% for l in locales %}
|
||||
<option value="{{ l.0 }}">{{ l.1 }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row control-group poweredby">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<form class="form-inline"
|
||||
action="{% url "control:event.vouchers.go" event=request.event.slug organizer=request.event.organizer.slug %}">
|
||||
<p class="input-group">
|
||||
<input type="text" name="code" class="form-control" placeholder="{% trans "Voucher code" %}" autofocus>
|
||||
<input type="text" name="code" class="form-control" placeholder="{% trans "Voucher code" %}">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="submit">{% trans "Go!" %}</button>
|
||||
</span>
|
||||
@@ -188,10 +188,7 @@
|
||||
</td>
|
||||
{% if request.event.has_subevents %}
|
||||
<td>
|
||||
{{ v.subevent.name }} – {{ v.subevent.get_date_range_display }}
|
||||
{% if request.event.settings.show_times %}
|
||||
{{ v.subevent.date_from|date:"TIME_FORMAT" }}
|
||||
{% endif %}
|
||||
{{ v.subevent.name }} – {{ v.subevent.get_date_range_display_with_times }}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="text-right flip">
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
import json
|
||||
import logging
|
||||
import operator
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
@@ -62,8 +63,9 @@ from django.http import (
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import conditional_escape
|
||||
from django.utils.html import conditional_escape, format_html
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext, gettext_lazy as _, gettext_noop
|
||||
from django.views.generic import FormView, ListView
|
||||
@@ -109,6 +111,8 @@ from ...helpers.format import (
|
||||
from ..logdisplay import OVERVIEW_BANLIST
|
||||
from . import CreateView, PaginationMixin, UpdateView
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EventSettingsViewMixin:
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -339,12 +343,29 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
def get_object(self, queryset=None) -> Event:
|
||||
return self.request.event
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
def available_plugins(self, event):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
return (p for p in get_all_plugins(event) if not p.name.startswith('.')
|
||||
and getattr(p, 'visible', True))
|
||||
|
||||
def prepare_links(self, pluginmeta, key):
|
||||
links = getattr(pluginmeta, key, [])
|
||||
try:
|
||||
return [
|
||||
(
|
||||
reverse(urlname, kwargs={"organizer": self.request.organizer.slug, "event": self.request.event.slug, **kwargs}),
|
||||
" > ".join(map(str, linktext)) if isinstance(linktext, tuple) else linktext,
|
||||
) for linktext, urlname, kwargs in links
|
||||
]
|
||||
except:
|
||||
logger.exception('Failed to resolve settings links.')
|
||||
return []
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
plugins = [p for p in get_all_plugins(self.object) if not p.name.startswith('.')
|
||||
and getattr(p, 'visible', True)]
|
||||
plugins = list(self.available_plugins(self.object))
|
||||
|
||||
order = [
|
||||
'FEATURE',
|
||||
'PAYMENT',
|
||||
@@ -375,12 +396,18 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
)
|
||||
plugins_grouped = [(c, list(plist)) for c, plist in plugins_grouped]
|
||||
|
||||
active_plugins = self.object.get_plugins()
|
||||
|
||||
def plugin_details(plugin):
|
||||
is_active = plugin.module in active_plugins
|
||||
settings_links = self.prepare_links(plugin, 'settings_links') if is_active else None
|
||||
navigation_links = self.prepare_links(plugin, 'navigation_links') if is_active else None
|
||||
return (plugin, is_active, settings_links, navigation_links)
|
||||
context['plugins'] = sorted([
|
||||
(c, labels.get(c, c), plist, any(getattr(p, 'picture', None) for p in plist))
|
||||
(c, labels.get(c, c), map(plugin_details, plist), any(getattr(p, 'picture', None) for p in plist))
|
||||
for c, plist
|
||||
in plugins_grouped
|
||||
], key=lambda c: (order.index(c[0]), c[1]) if c[0] in order else (999, str(c[1])))
|
||||
context['plugins_active'] = self.object.get_plugins()
|
||||
context['show_meta'] = settings.PRETIX_PLUGINS_SHOW_META
|
||||
return context
|
||||
|
||||
@@ -390,13 +417,10 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
return self.render_to_response(context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
self.object = self.get_object()
|
||||
|
||||
plugins_available = {
|
||||
p.module: p for p in get_all_plugins(self.object)
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True)
|
||||
p.module: p for p in self.available_plugins(self.object)
|
||||
}
|
||||
|
||||
with transaction.atomic():
|
||||
@@ -404,19 +428,38 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
if key.startswith("plugin:"):
|
||||
module = key.split(":")[1]
|
||||
if value == "enable" and module in plugins_available:
|
||||
if getattr(plugins_available[module], 'restricted', False):
|
||||
pluginmeta = plugins_available[module]
|
||||
if getattr(pluginmeta, 'restricted', False):
|
||||
if module not in request.event.settings.allowed_restricted_plugins:
|
||||
continue
|
||||
|
||||
self.request.event.log_action('pretix.event.plugins.enabled', user=self.request.user,
|
||||
data={'plugin': module})
|
||||
self.object.enable_plugin(module, allow_restricted=request.event.settings.allowed_restricted_plugins)
|
||||
|
||||
links = self.prepare_links(pluginmeta, 'settings_links')
|
||||
if links:
|
||||
info = [
|
||||
'<p>',
|
||||
format_html(_('The plugin {} is now active, you can configure it here:'),
|
||||
format_html("<strong>{}</strong>", pluginmeta.name)),
|
||||
'</p><p>',
|
||||
] + [
|
||||
format_html('<a href="{}" class="btn btn-default">{}</a> ', url, text)
|
||||
for url, text in links
|
||||
] + ['</p>']
|
||||
else:
|
||||
info = [
|
||||
format_html(_('The plugin {} is now active.'),
|
||||
format_html("<strong>{}</strong>", pluginmeta.name)),
|
||||
]
|
||||
messages.success(self.request, mark_safe("".join(info)))
|
||||
else:
|
||||
self.request.event.log_action('pretix.event.plugins.disabled', user=self.request.user,
|
||||
data={'plugin': module})
|
||||
self.object.disable_plugin(module)
|
||||
messages.success(self.request, _('The plugin has been disabled.'))
|
||||
self.object.save()
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
|
||||
@@ -65,7 +65,7 @@ from pretix.api.serializers.item import (
|
||||
from pretix.base.forms import I18nFormSet
|
||||
from pretix.base.models import (
|
||||
CartPosition, Item, ItemCategory, ItemVariation, Order, Question,
|
||||
QuestionAnswer, QuestionOption, Quota, Voucher,
|
||||
QuestionAnswer, QuestionOption, Quota, SeatCategoryMapping, Voucher,
|
||||
)
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.models.items import ItemAddOn, ItemBundle, ItemMetaValue
|
||||
@@ -101,10 +101,16 @@ class ItemList(ListView):
|
||||
template_name = 'pretixcontrol/items/index.html'
|
||||
|
||||
def get_queryset(self):
|
||||
requires_seat = Exists(
|
||||
SeatCategoryMapping.objects.filter(
|
||||
product_id=OuterRef('pk'),
|
||||
)
|
||||
)
|
||||
return Item.objects.filter(
|
||||
event=self.request.event
|
||||
).select_related("tax_rule").annotate(
|
||||
var_count=Count('variations')
|
||||
var_count=Count('variations'),
|
||||
requires_seat=requires_seat,
|
||||
).prefetch_related("category", "limit_sales_channels").order_by(
|
||||
F('category__position').asc(nulls_first=True),
|
||||
'category', 'position'
|
||||
|
||||
@@ -81,9 +81,9 @@ class EventList(PaginationMixin, ListView):
|
||||
max_to=Max('subevents__date_to'),
|
||||
max_fromto=Greatest(Max('subevents__date_to'), Max('subevents__date_from'))
|
||||
).annotate(
|
||||
order_from=Coalesce('min_from', 'date_from'),
|
||||
order_from=Coalesce('max_from', 'date_from'),
|
||||
order_to=Coalesce('max_fromto', 'max_to', 'max_from', 'date_to', 'date_from'),
|
||||
)
|
||||
).order_by("-order_from")
|
||||
|
||||
qs = qs.prefetch_related(
|
||||
Prefetch('quotas',
|
||||
|
||||
@@ -135,6 +135,7 @@ from pretix.control.views import PaginationMixin
|
||||
from pretix.helpers import OF_SELF
|
||||
from pretix.helpers.compat import CompatDeleteView
|
||||
from pretix.helpers.format import SafeFormatter, format_map
|
||||
from pretix.helpers.hierarkey import clean_filename
|
||||
from pretix.helpers.safedownload import check_token
|
||||
from pretix.presale.signals import question_form_fields
|
||||
|
||||
@@ -171,6 +172,26 @@ class OrderSearch(OrderSearchMixin, EventPermissionRequiredMixin, TemplateView):
|
||||
ctx['forms'] = self.get_forms()
|
||||
return ctx
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
all_valid = True
|
||||
for f in self.get_forms():
|
||||
if not f.is_valid():
|
||||
all_valid = False
|
||||
|
||||
if all_valid:
|
||||
data = request.POST.copy()
|
||||
data.pop('csrfmiddlewaretoken', None)
|
||||
return redirect(reverse(
|
||||
"control:event.orders",
|
||||
kwargs={
|
||||
"event": request.event.slug,
|
||||
"organizer": request.event.organizer.slug,
|
||||
}
|
||||
) + '?' + data.urlencode())
|
||||
else:
|
||||
messages.error(request, _("We could not process your input. See below for details."))
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class BaseOrderBulkActionView(OrderSearchMixin, EventPermissionRequiredMixin, AsyncFormView):
|
||||
template_name = 'pretixcontrol/orders/bulk_action.html'
|
||||
@@ -2364,6 +2385,9 @@ class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView):
|
||||
self.request.user, auto_email=False,
|
||||
attach_tickets=form.cleaned_data.get('attach_tickets', False),
|
||||
invoices=form.cleaned_data.get('attach_invoices', []),
|
||||
attach_other_files=[a for a in [
|
||||
self.request.event.settings.get('mail_attachment_new_order', as_type=str, default='')[len('file://'):]
|
||||
] if a] if form.cleaned_data.get('attach_new_order', False) else [],
|
||||
)
|
||||
messages.success(self.request,
|
||||
_('Your message has been queued and will be sent to {}.'.format(order.email)))
|
||||
@@ -2432,6 +2456,9 @@ class OrderPositionSendMail(OrderSendMail):
|
||||
'pretix.event.order.position.email.custom_sent',
|
||||
self.request.user,
|
||||
attach_tickets=form.cleaned_data.get('attach_tickets', False),
|
||||
attach_other_files=[a for a in [
|
||||
self.request.event.settings.get('mail_attachment_new_order', as_type=str, default='')[len('file://'):]
|
||||
] if a] if form.cleaned_data.get('attach_new_order', False) else [],
|
||||
)
|
||||
messages.success(self.request,
|
||||
_('Your message has been queued and will be sent to {}.'.format(position.attendee_email)))
|
||||
@@ -2461,6 +2488,23 @@ class OrderEmailHistory(EventPermissionRequiredMixin, OrderViewMixin, ListView):
|
||||
)
|
||||
return qs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
for l in ctx["logs"]:
|
||||
invoice_ids = l.parsed_data.get("invoices")
|
||||
if invoice_ids:
|
||||
if type(invoice_ids) is int:
|
||||
invoice_ids = [invoice_ids]
|
||||
l.parsed_invoices = Invoice.objects.filter(
|
||||
event=self.request.event,
|
||||
pk__in=invoice_ids,
|
||||
)
|
||||
if l.parsed_data.get("attach_other_files"):
|
||||
l.parsed_other_files = [
|
||||
clean_filename(os.path.basename(f)) for f in l.parsed_data["attach_other_files"]
|
||||
]
|
||||
return ctx
|
||||
|
||||
|
||||
class AnswerDownload(EventPermissionRequiredMixin, OrderViewMixin, ListView):
|
||||
permission = 'can_view_orders'
|
||||
|
||||
@@ -44,7 +44,9 @@ import dateutil
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.core.exceptions import (
|
||||
BadRequest, PermissionDenied, ValidationError,
|
||||
)
|
||||
from django.core.files import File
|
||||
from django.db import transaction
|
||||
from django.db.models import (
|
||||
@@ -204,9 +206,9 @@ class OrganizerDetail(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin
|
||||
max_to=Max('subevents__date_to'),
|
||||
max_fromto=Greatest(Max('subevents__date_to'), Max('subevents__date_from'))
|
||||
).annotate(
|
||||
order_from=Coalesce('min_from', 'date_from'),
|
||||
order_from=Coalesce('max_from', 'date_from'),
|
||||
order_to=Coalesce('max_fromto', 'max_to', 'max_from', 'date_to', 'date_from'),
|
||||
)
|
||||
).order_by("-order_from")
|
||||
if self.filter_form.is_valid():
|
||||
qs = self.filter_form.filter_qs(qs)
|
||||
return qs
|
||||
@@ -944,13 +946,16 @@ class DeviceQueryMixin:
|
||||
qs = self.request.organizer.devices.prefetch_related(
|
||||
'limit_events', 'gate',
|
||||
).order_by('revoked', '-device_id')
|
||||
if self.filter_form.is_valid():
|
||||
qs = self.filter_form.filter_qs(qs)
|
||||
|
||||
if 'device' in self.request_data and '__ALL' not in self.request_data:
|
||||
qs = qs.filter(
|
||||
id__in=self.request_data.getlist('device')
|
||||
)
|
||||
elif self.request.method == 'GET' or '__ALL' in self.request_data:
|
||||
if self.filter_form.is_valid():
|
||||
qs = self.filter_form.filter_qs(qs)
|
||||
else:
|
||||
raise BadRequest("No devices selected")
|
||||
|
||||
return qs
|
||||
|
||||
|
||||
@@ -104,9 +104,26 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
|
||||
scheme = PERSON_NAME_SCHEMES[self.request.event.settings.name_scheme]
|
||||
sample = {k: str(v) for k, v in scheme['sample'].items()}
|
||||
p = order.positions.create(item=item, attendee_name_parts=sample, price=item.default_price)
|
||||
order.positions.create(item=item2, attendee_name_parts=sample, price=item.default_price, addon_to=p)
|
||||
order.positions.create(item=item2, attendee_name_parts=sample, price=item.default_price, addon_to=p)
|
||||
p = order.positions.create(
|
||||
item=item,
|
||||
attendee_name_parts=sample,
|
||||
company=_("Sample company"),
|
||||
price=item.default_price
|
||||
)
|
||||
order.positions.create(
|
||||
item=item2,
|
||||
attendee_name_parts=sample,
|
||||
company=_("Sample company"),
|
||||
price=item.default_price,
|
||||
addon_to=p
|
||||
)
|
||||
order.positions.create(
|
||||
item=item2,
|
||||
attendee_name_parts=sample,
|
||||
company=_("Sample company"),
|
||||
price=item.default_price,
|
||||
addon_to=p
|
||||
)
|
||||
|
||||
InvoiceAddress.objects.create(order=order, name_parts=sample, company=_("Sample company"))
|
||||
return p
|
||||
|
||||
@@ -37,12 +37,12 @@ from zoneinfo import ZoneInfo
|
||||
|
||||
from dateutil.parser import parse
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db.models import F, Max, Min, Q
|
||||
from django.db.models import Count, F, Max, Min, Q
|
||||
from django.db.models.functions import Coalesce, Greatest
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse
|
||||
from django.utils.formats import date_format, get_format
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.timezone import make_aware
|
||||
from django.utils.translation import gettext as _, pgettext
|
||||
|
||||
@@ -56,7 +56,7 @@ from pretix.control.permissions import (
|
||||
event_permission_required, organizer_permission_required,
|
||||
)
|
||||
from pretix.helpers.daterange import daterange
|
||||
from pretix.helpers.i18n import i18ncomp
|
||||
from pretix.helpers.i18n import i18ncomp, parse_date_localized
|
||||
|
||||
|
||||
def serialize_user(u):
|
||||
@@ -326,6 +326,9 @@ def nav_context_list(request):
|
||||
qs_orga = Organizer.objects.filter(pk__in=request.user.teams.values_list('organizer', flat=True))
|
||||
if query:
|
||||
qs_orga = qs_orga.filter(Q(name__icontains=query) | Q(slug__icontains=query))
|
||||
qs_orga = qs_orga.annotate(
|
||||
n_events=Count("events")
|
||||
).order_by("-n_events")
|
||||
|
||||
if query and len(query) >= 3:
|
||||
qs_orders = Order.objects.filter(
|
||||
@@ -405,13 +408,7 @@ def subevent_select2(request, **kwargs):
|
||||
qf = Q(name__icontains=i18ncomp(query)) | Q(location__icontains=query)
|
||||
tz = request.event.timezone
|
||||
|
||||
dt = None
|
||||
for f in get_format('DATE_INPUT_FORMATS'):
|
||||
try:
|
||||
dt = datetime.strptime(query, f)
|
||||
break
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
dt = parse_date_localized(query)
|
||||
|
||||
if dt:
|
||||
dt_start = make_aware(datetime.combine(dt.date(), time(hour=0, minute=0, second=0)), tz)
|
||||
@@ -455,13 +452,7 @@ def quotas_select2(request, **kwargs):
|
||||
qf = Q(name__icontains=query) | Q(subevent__name__icontains=i18ncomp(query))
|
||||
tz = request.event.timezone
|
||||
|
||||
dt = None
|
||||
for f in get_format('DATE_INPUT_FORMATS'):
|
||||
try:
|
||||
dt = datetime.strptime(query, f)
|
||||
break
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
dt = parse_date_localized(query)
|
||||
|
||||
if dt and request.event.has_subevents:
|
||||
dt_start = make_aware(datetime.combine(dt.date(), time(hour=0, minute=0, second=0)), tz)
|
||||
|
||||
@@ -629,7 +629,7 @@ class VoucherBulkAction(EventPermissionRequiredMixin, View):
|
||||
log_entries.append(obj.log_action('pretix.voucher.changed', user=self.request.user, data={
|
||||
'max_usages': min(obj.redeemed, obj.max_usages),
|
||||
'bulk': True
|
||||
}), save=False)
|
||||
}, save=False))
|
||||
obj.max_usages = min(obj.redeemed, obj.max_usages)
|
||||
obj.save(update_fields=['max_usages'])
|
||||
|
||||
|
||||
@@ -33,7 +33,10 @@
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
from django.utils.html import format_html
|
||||
from django.utils.translation import get_language, gettext_lazy as _
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import (
|
||||
get_language, gettext_lazy as _, pgettext_lazy,
|
||||
)
|
||||
|
||||
from pretix.helpers.templatetags.date_fast import date_fast as _date
|
||||
|
||||
@@ -48,23 +51,28 @@ def daterange(df, dt, as_html=False):
|
||||
else:
|
||||
if as_html:
|
||||
base_format = format_html("<time datetime=\"{}\">{{}}</time>{{}}<time datetime=\"{}\">{{}}</time>", _date(df, "Y-m-d"), _date(dt, "Y-m-d"))
|
||||
until = format_html(
|
||||
" <span aria-hidden=\"true\">–</span><span class=\"sr-only\"> {until} </span> ",
|
||||
until=pgettext_lazy("timerange", "until")
|
||||
)
|
||||
else:
|
||||
base_format = "{}{}{}"
|
||||
until = " – "
|
||||
|
||||
if lng.startswith("de"):
|
||||
if df.year == dt.year and df.month == dt.month and df.day == dt.day:
|
||||
return format_html(base_format, _date(df, "D, j. F Y"))
|
||||
elif df.year == dt.year and df.month == dt.month:
|
||||
return format_html(base_format, _date(df, "j."), "–", _date(dt, "j. F Y"))
|
||||
return format_html(base_format, _date(df, "j."), mark_safe(until.strip()), _date(dt, "j. F Y"))
|
||||
elif df.year == dt.year:
|
||||
return format_html(base_format, _date(df, "j. F"), " – ", _date(dt, "j. F Y"))
|
||||
return format_html(base_format, _date(df, "j. F"), until, _date(dt, "j. F Y"))
|
||||
elif lng.startswith("en"):
|
||||
if df.year == dt.year and df.month == dt.month and df.day == dt.day:
|
||||
return format_html(base_format, _date(df, "D, N jS, Y"))
|
||||
elif df.year == dt.year and df.month == dt.month:
|
||||
return format_html(base_format, _date(df, "N jS"), " – ", _date(dt, "jS, Y"))
|
||||
return format_html(base_format, _date(df, "N jS"), until, _date(dt, "jS, Y"))
|
||||
elif df.year == dt.year:
|
||||
return format_html(base_format, _date(df, "N jS"), " – ", _date(dt, "N jS, Y"))
|
||||
return format_html(base_format, _date(df, "N jS"), until, _date(dt, "N jS, Y"))
|
||||
elif lng.startswith("es"):
|
||||
if df.year == dt.year and df.month == dt.month and df.day == dt.day:
|
||||
return format_html(base_format, _date(df, "DATE_FORMAT"))
|
||||
@@ -72,14 +80,14 @@ def daterange(df, dt, as_html=False):
|
||||
return format_html(
|
||||
base_format,
|
||||
_date(df, "j"),
|
||||
" - ",
|
||||
until,
|
||||
"{} de {} de {}".format(_date(dt, "j"), _date(dt, "F"), _date(dt, "Y"))
|
||||
)
|
||||
elif df.year == dt.year:
|
||||
return format_html(
|
||||
base_format,
|
||||
"{} de {}".format(_date(df, "j"), _date(df, "F")),
|
||||
" - ",
|
||||
until,
|
||||
"{} de {} de {}".format(_date(dt, "j"), _date(dt, "F"), _date(dt, "Y"))
|
||||
)
|
||||
|
||||
@@ -89,24 +97,31 @@ def daterange(df, dt, as_html=False):
|
||||
if as_html:
|
||||
base_format = "<time datetime=\"{}\">{}</time>"
|
||||
return format_html(
|
||||
"{date_from} – {date_to}",
|
||||
"{date_from}{until}{date_to}",
|
||||
date_from=format_html(base_format, _date(df, "Y-m-d"), _date(df, "DATE_FORMAT")),
|
||||
date_to=format_html(base_format, _date(dt, "Y-m-d"), _date(dt, "DATE_FORMAT")),
|
||||
until=until,
|
||||
)
|
||||
|
||||
return _("{date_from} – {date_to}").format(
|
||||
return _("{date_from}{until}{date_to}").format(
|
||||
date_from=_date(df, "DATE_FORMAT"),
|
||||
date_to=_date(dt, "DATE_FORMAT"),
|
||||
until=until,
|
||||
)
|
||||
|
||||
|
||||
def datetimerange(df, dt, as_html=False):
|
||||
if as_html:
|
||||
base_format = format_html("<time datetime=\"{}\">{{}}</time>{{}}<time datetime=\"{}\">{{}}</time>", _date(df, "Y-m-d H:i"), _date(dt, "Y-m-d H:i"))
|
||||
until = format_html(
|
||||
" <span aria-hidden=\"true\">–</span><span class=\"sr-only\"> {until} </span> ",
|
||||
until=pgettext_lazy("timerange", "until")
|
||||
)
|
||||
else:
|
||||
base_format = "{}{}{}"
|
||||
until = " – "
|
||||
|
||||
if df.year == dt.year and df.month == dt.month and df.day == dt.day:
|
||||
return format_html(base_format, _date(df, "SHORT_DATE_FORMAT") + " " + _date(df, "TIME_FORMAT"), " – ", _date(dt, "TIME_FORMAT"))
|
||||
return format_html(base_format, _date(df, "SHORT_DATE_FORMAT") + " " + _date(df, "TIME_FORMAT"), until, _date(dt, "TIME_FORMAT"))
|
||||
else:
|
||||
return format_html(base_format, _date(df, "SHORT_DATETIME_FORMAT"), " – ", _date(dt, "SHORT_DATETIME_FORMAT"))
|
||||
return format_html(base_format, _date(df, "SHORT_DATETIME_FORMAT"), until, _date(dt, "SHORT_DATETIME_FORMAT"))
|
||||
|
||||
@@ -23,7 +23,9 @@ import gettext as gettext_module
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
from functools import lru_cache
|
||||
from typing import Optional
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
@@ -222,3 +224,15 @@ def get_language_score(locale):
|
||||
else:
|
||||
score = len(list(catalog.items())) or 1
|
||||
return score
|
||||
|
||||
|
||||
def parse_date_localized(date_str) -> Optional[datetime]:
|
||||
"""Parses a date according to the localized date input formats. Returns None if invalid."""
|
||||
dt = None
|
||||
for f in get_format('DATE_INPUT_FORMATS'):
|
||||
try:
|
||||
dt = datetime.strptime(date_str, f)
|
||||
break
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
return dt
|
||||
|
||||
@@ -44,3 +44,13 @@ def modelcopy(obj: models.Model, **kwargs):
|
||||
else:
|
||||
setattr(n, f.name, copy.deepcopy(val))
|
||||
return n
|
||||
|
||||
|
||||
# django 5 contains this in django.utils.choices.flatten_choices
|
||||
def flatten_choices(choices):
|
||||
"""Flatten choices by removing nested values."""
|
||||
for value_or_group, label_or_nested in choices or ():
|
||||
if isinstance(label_or_nested, (list, tuple)):
|
||||
yield from label_or_nested
|
||||
else:
|
||||
yield value_or_group, label_or_nested
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="14" viewBox="0 0 4.7624999 3.7041668">
|
||||
<path
|
||||
d="m 1.9592032,1.8522629e-4 c -0.21468,0 -0.38861,0.17394000371 -0.38861,0.38861000371 0,0.21466 0.17393,0.38861 0.38861,0.38861 0.21468,0 0.3886001,-0.17395 0.3886001,-0.38861 0,-0.21467 -0.1739201,-0.38861000371 -0.3886001,-0.38861000371 z m 0.1049,0.84543000371 c -0.20823,-0.0326 -0.44367,0.12499 -0.39998,0.40462997 l 0.20361,1.01854 c 0.0306,0.15316 0.15301,0.28732 0.3483,0.28732 h 0.8376701 v 0.92708 c 0,0.29313 0.41187,0.29447 0.41187,0.005 v -1.19115 c 0,-0.14168 -0.0995,-0.29507 -0.29094,-0.29507 l -0.65578,-10e-4 -0.1757,-0.87644 C 2.3042533,0.95300523 2.1890432,0.86500523 2.0641032,0.84547523 Z m -0.58549,0.44906997 c -0.0946,-0.0134 -0.20202,0.0625 -0.17829,0.19172 l 0.18759,0.91054 c 0.0763,0.33956 0.36802,0.55914 0.66042,0.55914 h 0.6015201 c 0.21356,0 0.21448,-0.32143 -0.003,-0.32143 H 2.1954632 c -0.19911,0 -0.36364,-0.11898 -0.41341,-0.34107 l -0.17777,-0.87126 c -0.0165,-0.0794 -0.0688,-0.11963 -0.12557,-0.12764 z"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="14" viewBox="0 0 18 14">
|
||||
<path d="M7.713 3.573c-.787-.124-1.677.472-1.511 1.529l.857 3.473c.116.579.578 1.086 1.317 1.086h3.166v3.504c0 1.108 1.556 1.113 1.556.019V8.682c0-.536-.376-1.116-1.099-1.116L9.52 7.563l-.752-2.936c-.147-.648-.583-.981-1.055-1.055v.001Z"></path>
|
||||
<path d="M4.48 5.835a.6.6 0 0 0-.674.725l.71 3.441c.287 1.284 1.39 2.114 2.495 2.114h2.273c.807 0 .811-1.215-.01-1.215H7.188c-.753 0-1.375-.45-1.563-1.289l-.672-3.293c-.062-.3-.26-.452-.474-.483ZM7.433.102a1.468 1.468 0 1 0 0 2.937 1.469 1.469 0 1 0 0-2.937Z"></path>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 668 B |
+2887
-2747
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-29 13:18+0000\n"
|
||||
"POT-Creation-Date: 2025-04-28 11:08+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -547,52 +547,52 @@ msgstr ""
|
||||
msgid "Check-in QR"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:543
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:549
|
||||
msgid "The PDF background file could not be loaded for the following reason:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:903
|
||||
msgid "Group of objects"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:899
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:908
|
||||
msgid "Text object (deprecated)"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:901
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:910
|
||||
msgid "Text box"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:903
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:912
|
||||
msgid "Barcode area"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:905
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:914
|
||||
msgid "Image area"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:907
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:916
|
||||
msgid "Powered by pretix"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:909
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:918
|
||||
msgid "Object"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:913
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:922
|
||||
msgid "Ticket design"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1250
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1258
|
||||
msgid "Saving failed."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1319
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1370
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1327
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1378
|
||||
msgid "Error while uploading your PDF file, please try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1353
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1361
|
||||
msgid "Do you really want to leave the editor without saving your changes?"
|
||||
msgstr ""
|
||||
|
||||
@@ -608,54 +608,54 @@ msgstr ""
|
||||
msgid "Unknown error."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:318
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:330
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:322
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:334
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:326
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:338
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:442
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:462
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:454
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:474
|
||||
msgid "Search query"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:460
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:472
|
||||
msgid "All"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:461
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:473
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:465
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:477
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:808
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:820
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:811
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:823
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:969
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:981
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1021
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1084
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1102
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -663,6 +663,10 @@ msgstr ""
|
||||
msgid "Calculating default price…"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/plugins.js:69
|
||||
msgid "No results"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:42
|
||||
msgid "Others"
|
||||
msgstr ""
|
||||
@@ -693,32 +697,32 @@ msgid_plural "The items in your cart are reserved for you for {num} minutes."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:203
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:215
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:211
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:223
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:227
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:239
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:446
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:458
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:498
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:510
|
||||
msgid "required"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:554
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:574
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:552
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:565
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
@@ -888,109 +892,114 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:48
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgid "Checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"We could not create your cart, since there are currently too many users in "
|
||||
"this ticket shop. Please click \"Continue\" to retry in a new tab."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:53
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"You currently have an active cart for this event. If you select more "
|
||||
"products, they will be added to your existing cart."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"Some or all ticket categories are currently sold out. If you want, you can "
|
||||
@@ -998,83 +1007,111 @@ msgid ""
|
||||
"again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
msgid "Mo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
msgid "Tu"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
msgid "We"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:77
|
||||
msgid "Th"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:77
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
msgid "Fr"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||
msgid "Sa"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
||||
msgid "Su"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
||||
msgid "Monday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
||||
msgid "January"
|
||||
msgid "Tuesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
||||
msgid "February"
|
||||
msgid "Wednesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
||||
msgid "March"
|
||||
msgid "Thursday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||
msgid "April"
|
||||
msgid "Friday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||
msgid "May"
|
||||
msgid "Saturday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
||||
msgid "August"
|
||||
msgid "Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||
msgid "September"
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||
msgid "October"
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:92
|
||||
msgid "November"
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:93
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:95
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:96
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:97
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:98
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:99
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:100
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:101
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
+2986
-2762
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-29 13:18+0000\n"
|
||||
"POT-Creation-Date: 2025-04-28 11:08+0000\n"
|
||||
"PO-Revision-Date: 2021-09-15 11:22+0000\n"
|
||||
"Last-Translator: Mohamed Tawfiq <mtawfiq@wafyapp.com>\n"
|
||||
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -577,56 +577,56 @@ msgstr ""
|
||||
msgid "Check-in QR"
|
||||
msgstr "QR الدخول"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:543
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:549
|
||||
msgid "The PDF background file could not be loaded for the following reason:"
|
||||
msgstr "لا يمكن تحميل ملف PDF الخلفية للأسباب التالية:"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:903
|
||||
msgid "Group of objects"
|
||||
msgstr "مجموعة من العناصر"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:899
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:908
|
||||
#, fuzzy
|
||||
#| msgid "Text object"
|
||||
msgid "Text object (deprecated)"
|
||||
msgstr "عنصر نص"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:901
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:910
|
||||
#, fuzzy
|
||||
#| msgid "Text object"
|
||||
msgid "Text box"
|
||||
msgstr "عنصر نص"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:903
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:912
|
||||
msgid "Barcode area"
|
||||
msgstr "منطقة باركود"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:905
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:914
|
||||
msgid "Image area"
|
||||
msgstr "منطقة صورة"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:907
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:916
|
||||
msgid "Powered by pretix"
|
||||
msgstr "مدعوم من pretix"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:909
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:918
|
||||
msgid "Object"
|
||||
msgstr "عنصر"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:913
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:922
|
||||
msgid "Ticket design"
|
||||
msgstr "تصميم التذكرة"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1250
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1258
|
||||
msgid "Saving failed."
|
||||
msgstr "فشلت عملية الحفظ."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1319
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1370
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1327
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1378
|
||||
msgid "Error while uploading your PDF file, please try again."
|
||||
msgstr "حصل خطأ أثناء رفع ملف PDF الخاص بك، يرجى المحاولة مرة أخرى."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1353
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1361
|
||||
msgid "Do you really want to leave the editor without saving your changes?"
|
||||
msgstr "هل تريد أن تغادر المحرر دون حفظ التعديلات؟"
|
||||
|
||||
@@ -642,54 +642,54 @@ msgstr "توليد الرسائل …"
|
||||
msgid "Unknown error."
|
||||
msgstr "خطأ غير معروف."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:318
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:330
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr "اللون يتمتع بتباين كبير وتسهل قراءته!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:322
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:334
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr "اللون يحظى بتباين معقول ويمكن أن يكون مناسب للقراءة!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:326
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:338
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
msgstr "تباين اللون سيئ للخلفية البيضاء، الرجاء اختيار لون غامق."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:442
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:462
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:454
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:474
|
||||
msgid "Search query"
|
||||
msgstr "البحث في الاستفسارات"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:460
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:472
|
||||
msgid "All"
|
||||
msgstr "الكل"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:461
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:473
|
||||
msgid "None"
|
||||
msgstr "لا شيء"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:465
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:477
|
||||
msgid "Selected only"
|
||||
msgstr "المختارة فقط"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:808
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:820
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:811
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:823
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:969
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:981
|
||||
msgid "Use a different name internally"
|
||||
msgstr "قم باستخدم اسم مختلف داخليا"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1021
|
||||
msgid "Click to close"
|
||||
msgstr "اضغط لاغلاق الصفحة"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1084
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1102
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "لم تقم بحفظ التعديلات!"
|
||||
|
||||
@@ -697,6 +697,12 @@ msgstr "لم تقم بحفظ التعديلات!"
|
||||
msgid "Calculating default price…"
|
||||
msgstr "حساب السعر الافتراضي…"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/plugins.js:69
|
||||
#, fuzzy
|
||||
#| msgid "Search results"
|
||||
msgid "No results"
|
||||
msgstr "البحث في النتائج"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:42
|
||||
msgid "Others"
|
||||
msgstr "غير ذلك"
|
||||
@@ -741,32 +747,32 @@ msgstr[3] "سيتم حجز العناصر لك في سلة التسوق لعدة
|
||||
msgstr[4] "سيتم حجز العناصر لك في سلة التسوق لدقائق {num}."
|
||||
msgstr[5] "سيتم حجز العناصر لك في سلة التسوق لمدة {num}."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:203
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:215
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "يحصل المنظم على %(currency) %(amount)"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:211
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:223
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "ستسترد %(currency)%(amount)"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:227
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:239
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "الرجاء إدخال المبلغ الذي يمكن للمنظم الاحتفاظ به."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:446
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:458
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "الرجاء إدخال عدد لأحد أنواع التذاكر."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:498
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:510
|
||||
msgid "required"
|
||||
msgstr "مطلوب"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:554
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:574
|
||||
msgid "Time zone:"
|
||||
msgstr "المنطقة الزمنية:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:552
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:565
|
||||
msgid "Your local time:"
|
||||
msgstr "التوقيت المحلي:"
|
||||
|
||||
@@ -949,23 +955,31 @@ msgid "Open ticket shop"
|
||||
msgstr "إغلاق متجر التذاكر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:48
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "Resume checkout"
|
||||
msgctxt "widget"
|
||||
msgid "Checkout"
|
||||
msgstr "استئناف الدفع"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr "لا يمكن إنشاء سلة التسوق. الرجاء المحاولة مرة أخرى في وقت لاحق"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"We could not create your cart, since there are currently too many users in "
|
||||
"this ticket shop. Please click \"Continue\" to retry in a new tab."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr "قائمة الإنتظار"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:53
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"You currently have an active cart for this event. If you select more "
|
||||
@@ -974,37 +988,37 @@ msgstr ""
|
||||
"لديك الآن سلة تسوق مفعلة لهذا الحدث. إذا قمت باختيار منتجات أخرى سيتم "
|
||||
"إضافتها إلى سلة التسوق الموجودة حالياً."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr "استئناف الدفع"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr "استبدال قسيمة"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr "استبدال"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr "رمز القسيمة"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr "إغلاق"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr "استمرار"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "See variations"
|
||||
@@ -1012,7 +1026,7 @@ msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr "أنظر إلى الاختلافات"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "See variations"
|
||||
@@ -1020,47 +1034,47 @@ msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr "أنظر إلى الاختلافات"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr "اختر فعالية أخرى"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr "اختر تاريخا آخر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr "العودة"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr "الشهر القادم"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr "الشهر السابق"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr "الأسبوع القادم"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr "الأسبوع السابق"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr "خيارات المقاعد"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"Some or all ticket categories are currently sold out. If you want, you can "
|
||||
@@ -1068,86 +1082,114 @@ msgid ""
|
||||
"again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
||||
#, fuzzy
|
||||
#| msgid "Load more"
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr "تحميل المزيد"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
msgid "Mo"
|
||||
msgstr "الإثنين"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
msgid "Tu"
|
||||
msgstr "الثلاثاء"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
msgid "We"
|
||||
msgstr "الأربعاء"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:77
|
||||
msgid "Th"
|
||||
msgstr "الخميس"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:77
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
msgid "Fr"
|
||||
msgstr "الجمعة"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||
msgid "Sa"
|
||||
msgstr "السبت"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
||||
msgid "Su"
|
||||
msgstr "الأحد"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
||||
msgid "Monday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
||||
msgid "Tuesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
||||
msgid "Wednesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
||||
msgid "Thursday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||
msgid "Friday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||
msgid "Saturday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||
msgid "Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||
msgid "January"
|
||||
msgstr "يناير"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||
msgid "February"
|
||||
msgstr "فبراير"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:92
|
||||
msgid "March"
|
||||
msgstr "مارس"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:93
|
||||
msgid "April"
|
||||
msgstr "أبريل"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
||||
msgid "May"
|
||||
msgstr "مايو"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:95
|
||||
msgid "June"
|
||||
msgstr "يونيو"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:96
|
||||
msgid "July"
|
||||
msgstr "يوليو"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:97
|
||||
msgid "August"
|
||||
msgstr "أغسطس"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:98
|
||||
msgid "September"
|
||||
msgstr "سبتمبر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:99
|
||||
msgid "October"
|
||||
msgstr "أكتوبر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:92
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:100
|
||||
msgid "November"
|
||||
msgstr "نوفمبر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:93
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:101
|
||||
msgid "December"
|
||||
msgstr "ديسمبر"
|
||||
|
||||
|
||||
+2887
-2747
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-29 13:18+0000\n"
|
||||
"POT-Creation-Date: 2025-04-28 11:08+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -547,52 +547,52 @@ msgstr ""
|
||||
msgid "Check-in QR"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:543
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:549
|
||||
msgid "The PDF background file could not be loaded for the following reason:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:903
|
||||
msgid "Group of objects"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:899
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:908
|
||||
msgid "Text object (deprecated)"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:901
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:910
|
||||
msgid "Text box"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:903
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:912
|
||||
msgid "Barcode area"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:905
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:914
|
||||
msgid "Image area"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:907
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:916
|
||||
msgid "Powered by pretix"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:909
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:918
|
||||
msgid "Object"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:913
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:922
|
||||
msgid "Ticket design"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1250
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1258
|
||||
msgid "Saving failed."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1319
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1370
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1327
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1378
|
||||
msgid "Error while uploading your PDF file, please try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1353
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1361
|
||||
msgid "Do you really want to leave the editor without saving your changes?"
|
||||
msgstr ""
|
||||
|
||||
@@ -608,54 +608,54 @@ msgstr ""
|
||||
msgid "Unknown error."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:318
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:330
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:322
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:334
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:326
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:338
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:442
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:462
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:454
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:474
|
||||
msgid "Search query"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:460
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:472
|
||||
msgid "All"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:461
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:473
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:465
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:477
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:808
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:820
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:811
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:823
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:969
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:981
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1021
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1084
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1102
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -663,6 +663,10 @@ msgstr ""
|
||||
msgid "Calculating default price…"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/plugins.js:69
|
||||
msgid "No results"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:42
|
||||
msgid "Others"
|
||||
msgstr ""
|
||||
@@ -693,32 +697,32 @@ msgid_plural "The items in your cart are reserved for you for {num} minutes."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:203
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:215
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:211
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:223
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:227
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:239
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:446
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:458
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:498
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:510
|
||||
msgid "required"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:554
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:574
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:552
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:565
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
@@ -888,109 +892,114 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:48
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgid "Checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"We could not create your cart, since there are currently too many users in "
|
||||
"this ticket shop. Please click \"Continue\" to retry in a new tab."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:53
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"You currently have an active cart for this event. If you select more "
|
||||
"products, they will be added to your existing cart."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"Some or all ticket categories are currently sold out. If you want, you can "
|
||||
@@ -998,83 +1007,111 @@ msgid ""
|
||||
"again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
msgid "Mo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
msgid "Tu"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
msgid "We"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:77
|
||||
msgid "Th"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:77
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
msgid "Fr"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||
msgid "Sa"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
||||
msgid "Su"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
||||
msgid "Monday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
||||
msgid "January"
|
||||
msgid "Tuesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
||||
msgid "February"
|
||||
msgid "Wednesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
||||
msgid "March"
|
||||
msgid "Thursday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||
msgid "April"
|
||||
msgid "Friday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||
msgid "May"
|
||||
msgid "Saturday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
||||
msgid "August"
|
||||
msgid "Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||
msgid "September"
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||
msgid "October"
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:92
|
||||
msgid "November"
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:93
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:95
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:96
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:97
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:98
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:99
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:100
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:101
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
+2937
-2743
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-29 13:18+0000\n"
|
||||
"POT-Creation-Date: 2025-04-28 11:08+0000\n"
|
||||
"PO-Revision-Date: 2020-12-19 07:00+0000\n"
|
||||
"Last-Translator: albert <albert.serra.monner@gmail.com>\n"
|
||||
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -548,52 +548,52 @@ msgstr ""
|
||||
msgid "Check-in QR"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:543
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:549
|
||||
msgid "The PDF background file could not be loaded for the following reason:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:903
|
||||
msgid "Group of objects"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:899
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:908
|
||||
msgid "Text object (deprecated)"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:901
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:910
|
||||
msgid "Text box"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:903
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:912
|
||||
msgid "Barcode area"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:905
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:914
|
||||
msgid "Image area"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:907
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:916
|
||||
msgid "Powered by pretix"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:909
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:918
|
||||
msgid "Object"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:913
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:922
|
||||
msgid "Ticket design"
|
||||
msgstr "Disseny del tiquet"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1250
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1258
|
||||
msgid "Saving failed."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1319
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1370
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1327
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1378
|
||||
msgid "Error while uploading your PDF file, please try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1353
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1361
|
||||
msgid "Do you really want to leave the editor without saving your changes?"
|
||||
msgstr ""
|
||||
|
||||
@@ -609,54 +609,54 @@ msgstr ""
|
||||
msgid "Unknown error."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:318
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:330
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:322
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:334
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:326
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:338
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:442
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:462
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:454
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:474
|
||||
msgid "Search query"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:460
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:472
|
||||
msgid "All"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:461
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:473
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:465
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:477
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:808
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:820
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:811
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:823
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:969
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:981
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1021
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1084
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1102
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -664,6 +664,10 @@ msgstr ""
|
||||
msgid "Calculating default price…"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/plugins.js:69
|
||||
msgid "No results"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:42
|
||||
msgid "Others"
|
||||
msgstr ""
|
||||
@@ -698,34 +702,34 @@ msgid_plural "The items in your cart are reserved for you for {num} minutes."
|
||||
msgstr[0] "El contingut de la cistella ja no el teniu reservat."
|
||||
msgstr[1] "El contingut de la cistella ja no el teniu reservat."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:203
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:215
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:211
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:223
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:227
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:239
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:446
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:458
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:498
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:510
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "required"
|
||||
msgstr "Cistella expirada"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:554
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:574
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:552
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:565
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
@@ -895,109 +899,114 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:48
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgid "Checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"We could not create your cart, since there are currently too many users in "
|
||||
"this ticket shop. Please click \"Continue\" to retry in a new tab."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:53
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"You currently have an active cart for this event. If you select more "
|
||||
"products, they will be added to your existing cart."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"Some or all ticket categories are currently sold out. If you want, you can "
|
||||
@@ -1005,83 +1014,111 @@ msgid ""
|
||||
"again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
msgid "Mo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
msgid "Tu"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
msgid "We"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:77
|
||||
msgid "Th"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:77
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
msgid "Fr"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||
msgid "Sa"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
||||
msgid "Su"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
||||
msgid "Monday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
||||
msgid "January"
|
||||
msgid "Tuesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
||||
msgid "February"
|
||||
msgid "Wednesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
||||
msgid "March"
|
||||
msgid "Thursday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||
msgid "April"
|
||||
msgid "Friday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||
msgid "May"
|
||||
msgid "Saturday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
||||
msgid "August"
|
||||
msgid "Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||
msgid "September"
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||
msgid "October"
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:92
|
||||
msgid "November"
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:93
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:95
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:96
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:97
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:98
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:99
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:100
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:101
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
+2985
-2740
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-29 13:18+0000\n"
|
||||
"POT-Creation-Date: 2025-04-28 11:08+0000\n"
|
||||
"PO-Revision-Date: 2025-02-19 17:00+0000\n"
|
||||
"Last-Translator: Petr Čermák <pcermak@live.com>\n"
|
||||
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -568,56 +568,56 @@ msgstr ""
|
||||
msgid "Check-in QR"
|
||||
msgstr "Check-in QR kód"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:543
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:549
|
||||
msgid "The PDF background file could not be loaded for the following reason:"
|
||||
msgstr "Pozadí PDF nemohl být načten:"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:903
|
||||
msgid "Group of objects"
|
||||
msgstr "Skupina objektů"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:899
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:908
|
||||
#, fuzzy
|
||||
#| msgid "Text object"
|
||||
msgid "Text object (deprecated)"
|
||||
msgstr "Textový objekt"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:901
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:910
|
||||
#, fuzzy
|
||||
#| msgid "Text object"
|
||||
msgid "Text box"
|
||||
msgstr "Textový objekt"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:903
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:912
|
||||
msgid "Barcode area"
|
||||
msgstr "Oblast s QR kódem"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:905
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:914
|
||||
msgid "Image area"
|
||||
msgstr "Oblast obrazu"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:907
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:916
|
||||
msgid "Powered by pretix"
|
||||
msgstr "Poháněno společností pretix"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:909
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:918
|
||||
msgid "Object"
|
||||
msgstr "Objekt"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:913
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:922
|
||||
msgid "Ticket design"
|
||||
msgstr "Design vstupenky"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1250
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1258
|
||||
msgid "Saving failed."
|
||||
msgstr "Uložení se nepodařilo."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1319
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1370
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1327
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1378
|
||||
msgid "Error while uploading your PDF file, please try again."
|
||||
msgstr "Při nahrávání souboru PDF došlo k problému, zkuste to prosím znovu."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1353
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1361
|
||||
msgid "Do you really want to leave the editor without saving your changes?"
|
||||
msgstr "Opravdu chcete opustit editor bez uložení změn?"
|
||||
|
||||
@@ -633,16 +633,16 @@ msgstr "Vytváření zpráv…"
|
||||
msgid "Unknown error."
|
||||
msgstr "Neznámá chyba."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:318
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:330
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr "Tato barva má velmi dobrý kontrast a je velmi dobře čitelná!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:322
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:334
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
"Tato barva má slušný kontrast a pravděpodobně je dostatečně dobře čitelná!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:326
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:338
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
@@ -650,40 +650,40 @@ msgstr ""
|
||||
"Tato barva je pro text na bílém pozadí špatně kontrastní, zvolte prosím "
|
||||
"tmavší odstín."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:442
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:462
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:454
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:474
|
||||
msgid "Search query"
|
||||
msgstr "Hledaný výraz"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:460
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:472
|
||||
msgid "All"
|
||||
msgstr "Všechny"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:461
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:473
|
||||
msgid "None"
|
||||
msgstr "Žádný"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:465
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:477
|
||||
msgid "Selected only"
|
||||
msgstr "Pouze vybrané"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:808
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:820
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:811
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:823
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:969
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:981
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Interně používat jiný název"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1021
|
||||
msgid "Click to close"
|
||||
msgstr "Kliknutím zavřete"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1084
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1102
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Máte neuložené změny!"
|
||||
|
||||
@@ -691,6 +691,12 @@ msgstr "Máte neuložené změny!"
|
||||
msgid "Calculating default price…"
|
||||
msgstr "Výpočet standardní ceny…"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/plugins.js:69
|
||||
#, fuzzy
|
||||
#| msgid "Search results"
|
||||
msgid "No results"
|
||||
msgstr "Vyhledat výsledky"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:42
|
||||
msgid "Others"
|
||||
msgstr "Další"
|
||||
@@ -728,32 +734,32 @@ msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
"Produkty v nákupním košíku jsou pro vás rezervovány na dalších {num} minut."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:203
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:215
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr "Organizátor si ponechává %(currency)s %(amount)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:211
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:223
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr "Dostanete %(currency)s %(amount)s zpět"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:227
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:239
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Zadejte částku, kterou si organizátor může ponechat."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:446
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:458
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Zadejte prosím množství pro jeden z typů vstupenek."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:498
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:510
|
||||
msgid "required"
|
||||
msgstr "povinný"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:554
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:574
|
||||
msgid "Time zone:"
|
||||
msgstr "Časové pásmo:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:552
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:565
|
||||
msgid "Your local time:"
|
||||
msgstr "Místní čas:"
|
||||
|
||||
@@ -936,11 +942,19 @@ msgid "Open ticket shop"
|
||||
msgstr "Obchod vstupenek otevřit"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:48
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "Resume checkout"
|
||||
msgctxt "widget"
|
||||
msgid "Checkout"
|
||||
msgstr "Obnovit checkout"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr "Nákupní košík se nepodařilo vytvořit. Zkuste to prosím znovu"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"We could not create your cart, since there are currently too many users in "
|
||||
@@ -950,12 +964,12 @@ msgstr ""
|
||||
"mnoho uživatelů. Klikněte prosím na \"Pokračovat\" a zkuste to znovu na nové "
|
||||
"kartě."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr "Čekací listina"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:53
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"You currently have an active cart for this event. If you select more "
|
||||
@@ -964,37 +978,37 @@ msgstr ""
|
||||
"V současné době máte aktivní košík pro tuto událost. Pokud vyberete další "
|
||||
"produkty, budou přidány do vašeho košíku."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr "Obnovit checkout"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr "Uplatnit poukázku"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr "Uplatnit"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr "Kód poukázky"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr "Zavřít"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr "Pokračovat"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "See variations"
|
||||
@@ -1002,7 +1016,7 @@ msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr "Zobrazit možnosti"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "See variations"
|
||||
@@ -1010,47 +1024,47 @@ msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr "Zobrazit možnosti"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr "Vybrat jinou událost"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr "Vybrat jiný datum"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr "Zpět"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr "Následující měsíc"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr "Předchozí měsíc"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr "Příští týden"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr "Předchozí týden"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr "Otevřete výběr sedadla"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"Some or all ticket categories are currently sold out. If you want, you can "
|
||||
@@ -1061,84 +1075,112 @@ msgstr ""
|
||||
"Pokud chcete, můžete se přidat na čekací listinu. Poté vás budeme "
|
||||
"informovat, pokud budou místa opět volná."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr "Načíst více"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
msgid "Mo"
|
||||
msgstr "Po"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
msgid "Tu"
|
||||
msgstr "Út"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
msgid "We"
|
||||
msgstr "St"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:77
|
||||
msgid "Th"
|
||||
msgstr "Čt"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:77
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
msgid "Fr"
|
||||
msgstr "Pá"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||
msgid "Sa"
|
||||
msgstr "So"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
||||
msgid "Su"
|
||||
msgstr "Ne"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
||||
msgid "Monday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
||||
msgid "Tuesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
||||
msgid "Wednesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
||||
msgid "Thursday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||
msgid "Friday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||
msgid "Saturday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||
msgid "Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||
msgid "January"
|
||||
msgstr "Leden"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||
msgid "February"
|
||||
msgstr "Únor"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:92
|
||||
msgid "March"
|
||||
msgstr "Březen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:93
|
||||
msgid "April"
|
||||
msgstr "Duben"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
||||
msgid "May"
|
||||
msgstr "Květen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:95
|
||||
msgid "June"
|
||||
msgstr "červen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:96
|
||||
msgid "July"
|
||||
msgstr "Červenec"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:97
|
||||
msgid "August"
|
||||
msgstr "Srpen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:98
|
||||
msgid "September"
|
||||
msgstr "Září"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:99
|
||||
msgid "October"
|
||||
msgstr "Říjen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:92
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:100
|
||||
msgid "November"
|
||||
msgstr "Listopad"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:93
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:101
|
||||
msgid "December"
|
||||
msgstr "Prosinec"
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user