Compare commits

..

3 Commits

Author SHA1 Message Date
Raphael Michel 8957ed929a Update src/pretix/base/channels.py 2023-11-09 10:09:07 +01:00
Richard Schreiber a24a36b1e7 sort asc, force web first 2023-11-08 12:29:18 +01:00
Richard Schreiber 13ee2b3c5d Control: sort sales-channels desc 2023-11-08 12:27:26 +01:00
246 changed files with 111893 additions and 132259 deletions
+1 -2
View File
@@ -276,8 +276,7 @@ Restarting the service can take a few seconds, especially if the update requires
Replace ``stable`` above with a specific version number like ``1.0`` or with ``latest`` for the development
version, if you want to.
Make sure to also read :ref:`update_notes` and the release notes of the version you are updating to. Pay special
attention to the "Runtime and server environment" section of all release notes between your current and new version.
Make sure to also read :ref:`update_notes` and the release notes of the version you are updating to.
.. _`docker_plugininstall`:
+1 -2
View File
@@ -286,8 +286,7 @@ To upgrade to a new pretix release, pull the latest code changes and run the fol
(venv)$ python -m pretix updatestyles
# systemctl restart pretix-web pretix-worker
Make sure to also read :ref:`update_notes` and the release notes of the version you are updating to. Pay special
attention to the "Runtime and server environment" section of all release notes between your current and new version.
Make sure to also read :ref:`update_notes` and the release notes of the version you are updating to.
.. _`manual_plugininstall`:
-25
View File
@@ -47,30 +47,5 @@ Or, with a docker installation::
$ docker exec -it pretix.service pretix create_order_transactions
Upgrade to 2023.6.0 or newer
""""""""""""""""""""""""""""
MariaDB and MySQL are no longer supported.
Upgrade to 2023.8.0 or newer
""""""""""""""""""""""""""""
PostgreSQL 11 is now required.
Upgrade to 2023.9.0 or newer
""""""""""""""""""""""""""""
This release includes a migration that changes the `id` column of all core database tables from `integer`
to `bigint`. If you have a large database, the migration step of the upgrade might take significantly longer than
usual, so plan the update accordingly.
The default value for the `registration` setting in `pretix.cfg` has changed to `false`.
Upgrade to 2023.10.0 or newer
"""""""""""""""""""""""""""""
This release includes a migration that changes retroactively fills an `organizer` column in the table
`pretixbase_logentry`. If you have a large database, the migration step of the upgrade might take significantly
longer than usual, so plan the update accordingly.
.. _blog: https://pretix.eu/about/en/blog/
+1 -9
View File
@@ -31,7 +31,6 @@ Checking a ticket in
This endpoint supports passing multiple check-in lists to perform a multi-event scan. However, each check-in list
passed needs to be from a distinct event.
:query string expand: Expand a field inside the ``position`` object into a full object. Currently ``subevent``, ``item``, ``variation``, and ``answers.question`` are supported. Can be passed multiple times.
:<json string secret: Scanned QR code corresponding to the ``secret`` attribute of a ticket.
:<json string source_type: Type of source the ``secret`` was obtained form. Defaults to ``"barcode"``.
:<json array lists: List of check-in list IDs to search on. No two check-in lists may be from the same event.
@@ -64,7 +63,6 @@ Checking a ticket in
``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.
:>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),
including the attributes ``id``, ``name``, ``event``, ``subevent``, and ``include_pending``.
:>json object questions: List of questions to be answered for check-in, only set on status ``"incomplete"``.
@@ -105,7 +103,6 @@ Checking a ticket in
},
"require_attention": false,
"checkin_texts": [],
"list": {
"id": 1,
"name": "Default check-in list",
@@ -128,7 +125,6 @@ Checking a ticket in
},
"require_attention": false,
"checkin_texts": [],
"list": {
"id": 1,
"name": "Default check-in list",
@@ -146,7 +142,6 @@ Checking a ticket in
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": true,
"show_during_checkin": true,
"options": [
{
"id": 1,
@@ -183,8 +178,7 @@ Checking a ticket in
"status": "error",
"reason": "invalid",
"reason_explanation": null,
"require_attention": false,
"checkin_texts": []
"require_attention": false
}
**Example error response (known, but invalid ticket)**:
@@ -199,7 +193,6 @@ Checking a ticket in
"reason": "unpaid",
"reason_explanation": null,
"require_attention": false,
"checkin_texts": [],
"list": {
"id": 1,
"name": "Default check-in list",
@@ -224,7 +217,6 @@ Checking a ticket in
* ``rules`` - Check-in prevented by a user-defined rule.
* ``ambiguous`` - Multiple tickets match scan, rejected.
* ``revoked`` - Ticket code has been revoked.
* ``unapproved`` - Order has not yet been approved.
* ``error`` - Internal error.
In case of reason ``rules`` and ``invalid_time``, there might be an additional response field ``reason_explanation``
+2 -5
View File
@@ -498,7 +498,7 @@ Order position endpoints
``attendee_name,positionid``
:query string order: Only return positions of the order with the given order code
:query string search: Fuzzy search matching the attendee name, order code, invoice address name as well as to the beginning of the secret.
:query string expand: Expand a field into a full object. Currently ``subevent``, ``item``, ``variation``, and ``answers.question`` are supported. Can be passed multiple times.
:query string expand: Expand a field into a full object. Currently only ``subevent``, ``item``, and ``variation`` are supported. Can be passed multiple times.
:query integer item: Only return positions with the purchased item matching the given ID.
:query integer item__in: Only return positions with the purchased item matching one of the given comma-separated IDs.
:query integer variation: Only return positions with the purchased item variation matching the given ID.
@@ -632,8 +632,7 @@ Order position endpoints
set this to ``false``. In that case, questions will just be ignored. Defaults
to ``true``.
:<json boolean canceled_supported: When this parameter is set to ``true``, the response code ``canceled`` may be
returned. Otherwise, canceled orders will return ``unpaid``. (**Deprecated**, in
the future, this will be ignored and ``canceled`` may always be returned.)
returned. Otherwise, canceled orders will return ``unpaid``.
:<json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
:<json boolean force: Specifies that the check-in should succeed regardless of revoked barcode, previous check-ins or required
questions that have not been filled. This is usually used to upload offline scans that already happened,
@@ -707,7 +706,6 @@ Order position endpoints
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": true,
"show_during_checkin": true,
"options": [
{
"id": 1,
@@ -760,7 +758,6 @@ Order position endpoints
* ``rules`` - Check-in prevented by a user-defined rule.
* ``ambiguous`` - Multiple tickets match scan, rejected.
* ``revoked`` - Ticket code has been revoked.
* ``unapproved`` - Order has not yet been approved.
In case of reason ``rules`` or ``invalid_time``, there might be an additional response field ``reason_explanation``
with a human-readable description of the violated rules. However, that field can also be missing or be ``null``.
-2
View File
@@ -565,8 +565,6 @@ organizer level.
.. warning:: This API is intended for advanced users. Even though we take care to validate your input, you will be
able to break your event using this API by creating situations of conflicting settings. Please take care.
.. note:: When authenticating with :ref:`rest-deviceauth`, only a limited subset of settings is available.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/settings/
Get current values of event settings.
-10
View File
@@ -29,8 +29,6 @@ position integer An integer, use
checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if such
a variation is being scanned.
checkin_text string Text that will be shown if a ticket of this type is
scanned (or ``null``).
require_approval boolean If ``true``, orders with this variation will need to be
approved by the event organizer before they can be
paid.
@@ -60,8 +58,6 @@ meta_data object Values set for
.. versionchanged:: 2023.10
The ``free_price_suggestion`` attribute has been added.
The ``checkin_text`` attribute has been added.
Endpoints
---------
@@ -98,7 +94,6 @@ Endpoints
},
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
@@ -124,7 +119,6 @@ Endpoints
},
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
@@ -185,7 +179,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
@@ -225,7 +218,6 @@ Endpoints
"default_price": "10.00",
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
@@ -256,7 +248,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
@@ -318,7 +309,6 @@ Endpoints
"free_price_suggestion": null,
"active": false,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
-20
View File
@@ -74,8 +74,6 @@ max_per_order integer This product
checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if such
a product is being scanned.
checkin_text string Text that will be shown if a ticket of this type is
scanned (or ``null``).
original_price money (string) An original price, shown for comparison, not used
for price calculations (or ``null``).
require_approval boolean If ``true``, orders with this product will need to be
@@ -139,8 +137,6 @@ variations list of objects A list with o
├ checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if such
a variation is being scanned.
├ checkin_text string Text that will be shown if a ticket of this type is
scanned (or ``null``).
├ require_approval boolean If ``true``, orders with this variation will need to be
approved by the event organizer before they can be
paid.
@@ -209,7 +205,6 @@ meta_data object Values set fo
.. versionchanged:: 2023.10
The ``checkin_text`` and ``variations[x].checkin_text`` attributes have been added.
The ``free_price_suggestion`` and ``variations[x].free_price_suggestion`` attributes have been added.
.. versionchanged:: 2023.10
@@ -288,7 +283,6 @@ Endpoints
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
"checkin_text": null,
"has_variations": false,
"generate_tickets": null,
"allow_waitinglist": true,
@@ -318,7 +312,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
@@ -338,7 +331,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
@@ -429,7 +421,6 @@ Endpoints
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
"checkin_text": null,
"has_variations": false,
"require_approval": false,
"require_bundling": false,
@@ -456,7 +447,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
@@ -476,7 +466,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
@@ -548,7 +537,6 @@ Endpoints
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_bundling": false,
"require_membership": false,
@@ -574,7 +562,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
@@ -594,7 +581,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
@@ -654,7 +640,6 @@ Endpoints
"allow_waitinglist": true,
"show_quota_left": null,
"checkin_attention": false,
"checkin_text": null,
"has_variations": true,
"require_approval": false,
"require_bundling": false,
@@ -681,7 +666,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
@@ -701,7 +685,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
@@ -792,7 +775,6 @@ Endpoints
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
"checkin_text": null,
"has_variations": true,
"require_approval": false,
"require_bundling": false,
@@ -819,7 +801,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
@@ -839,7 +820,6 @@ Endpoints
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
-11
View File
@@ -46,8 +46,6 @@ custom_followup_at date Internal date f
checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if a ticket
of this order is scanned.
checkin_text string Text that will be shown if a ticket of this order is
scanned (or ``null``).
invoice_address object Invoice address information (can be ``null``)
├ last_modified datetime Last modification date of the address
├ company string Customer company name
@@ -137,10 +135,6 @@ last_modified datetime Last modificati
The ``event`` attribute has been added. The organizer-level endpoint has been added.
.. versionchanged:: 2023.10
The ``checkin_text`` attribute has been added.
.. versionchanged:: 2023.9
The ``customer`` query parameter has been added.
@@ -324,7 +318,6 @@ List of all orders
"comment": "",
"custom_followup_at": null,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"valid_if_pending": false,
"invoice_address": {
@@ -546,7 +539,6 @@ Fetching individual orders
"comment": "",
"custom_followup_at": null,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"valid_if_pending": false,
"invoice_address": {
@@ -717,8 +709,6 @@ Updating order fields
* ``checkin_attention``
* ``checkin_text``
* ``locale``
* ``comment``
@@ -934,7 +924,6 @@ Creating orders
* ``comment`` (optional)
* ``custom_followup_at`` (optional)
* ``checkin_attention`` (optional)
* ``checkin_text`` (optional)
* ``require_approval`` (optional)
* ``valid_if_pending`` (optional)
* ``invoice_address`` (optional)
-11
View File
@@ -44,8 +44,6 @@ identifier string An arbitrary st
ask_during_checkin boolean If ``true``, this question will not be asked while
buying the ticket, but will show up when redeeming
the ticket instead.
show_during_checkin boolean If ``true``, the answer to the question will be shown
during check-in (if the check-in client supports it).
hidden boolean If ``true``, the question will only be shown in the
backend.
print_on_invoice boolean If ``true``, the question will only be shown on
@@ -79,10 +77,6 @@ dependency_value string An old version
for one value. **Deprecated.**
===================================== ========================== =======================================================
.. versionchanged:: 2023.8
The ``show_during_checkin`` attribute has been added.
Endpoints
---------
@@ -121,7 +115,6 @@ Endpoints
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"show_during_checkin": false,
"hidden": false,
"print_on_invoice": false,
"valid_number_min": null,
@@ -201,7 +194,6 @@ Endpoints
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"show_during_checkin": false,
"hidden": false,
"print_on_invoice": false,
"valid_number_min": null,
@@ -265,7 +257,6 @@ Endpoints
"items": [1, 2],
"position": 1,
"ask_during_checkin": false,
"show_during_checkin": false,
"hidden": false,
"print_on_invoice": false,
"dependency_question": null,
@@ -302,7 +293,6 @@ Endpoints
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"show_during_checkin": false,
"hidden": false,
"print_on_invoice": false,
"dependency_question": null,
@@ -386,7 +376,6 @@ Endpoints
"position": 2,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"show_during_checkin": false,
"hidden": false,
"print_on_invoice": false,
"dependency_question": null,
+1 -1
View File
@@ -11,7 +11,7 @@ Core
----
.. automodule:: pretix.base.signals
:members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types, notification,
:members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types,
item_copy_data, register_sales_channels, register_global_settings, quota_availability, global_email_filter,
register_ticket_secret_generators, gift_card_transaction_display
Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

After

Width:  |  Height:  |  Size: 278 KiB

+14 -14
View File
@@ -25,27 +25,27 @@ partition "data-based check" {
else
-down->[yes] "Is one or more block set on the ticket?"
--> if "" then
-right->[yes] "Return error BLOCKED"
-right->[no] "Return error BLOCKED"
else
-down->[no] "Is the order in status PENDING and not yet approved?"
-down->[yes] "If this is not an exit, is the valid_from/valid_until\nconstraint on the ticket fulfilled?"
--> if "" then
-right->[yes] "Return error UNAPPROVED"
-right->[no] "Return error INVALID_TIME"
else
-down->[no] "If this is not an exit, is the valid_from/valid_until\nconstraint on the ticket fulfilled?"
-down->[yes] "Is the product part of the check-in list?"
--> if "" then
-right->[no] "Return error INVALID_TIME"
-right->[no] "Return error PRODUCT"
else
-down->[yes] "Is the product part of the check-in list?"
-down->[yes] "Is the subevent part of the check-in list?"
--> if "" then
-right->[no] "Return error PRODUCT"
-right->[no] "Return error INVALID"
note bottom: TODO\ninconsistent\nwith online\ncheck
else
-down->[yes] "Is the subevent part of the check-in list?"
-down->[yes] "Is the order in status PAID?"
--> if "" then
-right->[no] "Return error INVALID"
note bottom: TODO\ninconsistent\nwith online\ncheck
else
-down->[yes] "Is the order in status PAID?"
-right->[no] "Is Order.require_approval set?"
--> if "" then
-->[yes] "Return error UNPAID "
else
-right->[no] "Is Order.valid_if_pending set?"
--> if "" then
-->[yes] "Is this an entry or exit?"
@@ -62,9 +62,9 @@ partition "data-based check" {
endif
endif
endif
else
-down->[yes] "Is this an entry or exit?"
endif
else
-down->[yes] "Is this an entry or exit?"
endif
endif
endif
Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

After

Width:  |  Height:  |  Size: 180 KiB

+12 -12
View File
@@ -42,25 +42,23 @@ endif
else
-down->[yes || force] "Is one or more block set on the ticket?"
--> if "" then
-right->[yes && !force] "Return error BLOCKED"
-right->[no && !force] "Return error BLOCKED"
else
-down->[no || force] "Is the order in status PENDING and not yet approved?"
-down->[yes || force] "If this is not an exit, is the valid_from/valid_until\nconstraint on the ticket fulfilled?"
--> if "" then
-right->[yes && !force] "Return error UNAPPROVED"
-right->[no && !force] "Return error INVALID_TIME"
else
-down->[no || force] "If this is not an exit, is the valid_from/valid_until\nconstraint on the ticket fulfilled?"
-down->[yes || force] "Is the product part of the check-in list?"
--> if "" then
-right->[no && !force] "Return error INVALID_TIME"
-right->[no && !force] "Return error PRODUCT"
else
-down->[yes || force] "Is the product part of the check-in list?"
-down->[yes || force] "Is the subevent part of the check-in list?"
--> if "" then
-right->[no && !force] "Return error PRODUCT"
-right->[no && !force] "Return error PRODUCT "
else
-down->[yes || force] "Is the subevent part of the check-in list?"
-down->[yes] "Is the order in status PAID?"
--> if "" then
-right->[no && !force] "Return error PRODUCT "
else
-down->[yes] "Is the order in status PAID?"
-right->[no && !force] "Is Order.require_approval set?"
--> if "" then
-->[no] "Is Order.valid_if_pending set?"
--> if "" then
@@ -79,8 +77,10 @@ else
endif
endif
else
-down->[yes || force] "Is this an entry or exit?\nIs the upload forced?"
-->[yes] "Return error UNPAID "
endif
else
-down->[yes || force] "Is this an entry or exit?\nIs the upload forced?"
endif
endif
endif
-4
View File
@@ -196,10 +196,6 @@ settings. For example, if you set up a meta data property called "Promoted" that
<pretix-widget event="https://pretix.eu/demo/series/" list-type="list" filter="attr[Promoted]=Yes"></pretix-widget>
If you have enabled public filters in your meta data attribute configuration, a filter formshows up. To disable, use::
<pretix-widget event="https://pretix.eu/demo/democon/" disable-filters></pretix-widget>
pretix Button
-------------
+1 -1
View File
@@ -19,4 +19,4 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
__version__ = "2023.11.0.dev0"
__version__ = "2023.10.0.dev0"
+1 -1
View File
@@ -109,7 +109,7 @@ LANGUAGES_RTL = {
'ar', 'hw'
}
LANGUAGES_INCUBATING = {
'fi', 'pt-br', 'gl',
'pl', 'fi', 'pt-br', 'gl',
}
LOCALE_PATHS = [
os.path.join(os.path.dirname(__file__), 'locale'),
+4 -7
View File
@@ -230,8 +230,8 @@ class EventSerializer(I18nAwareModelSerializer):
for key, v in value['meta_data'].items():
if key not in self.meta_properties:
raise ValidationError(_('Meta data property \'{name}\' does not exist.').format(name=key))
if self.meta_properties[key].choices:
if v not in self.meta_properties[key].choice_keys:
if self.meta_properties[key].allowed_values:
if v not in [_v.strip() for _v in self.meta_properties[key].allowed_values.splitlines()]:
raise ValidationError(_('Meta data property \'{name}\' does not allow value \'{value}\'.').format(name=key, value=v))
return value
@@ -528,8 +528,8 @@ class SubEventSerializer(I18nAwareModelSerializer):
for key, v in value['meta_data'].items():
if key not in self.meta_properties:
raise ValidationError(_('Meta data property \'{name}\' does not exist.').format(name=key))
if self.meta_properties[key].choices:
if v not in self.meta_properties[key].choice_keys:
if self.meta_properties[key].allowed_values:
if v not in [_v.strip() for _v in self.meta_properties[key].allowed_values.splitlines()]:
raise ValidationError(_('Meta data property \'{name}\' does not allow value \'{value}\'.').format(name=key, value=v))
return value
@@ -705,7 +705,6 @@ class EventSettingsSerializer(SettingsSerializer):
'frontpage_subevent_ordering',
'event_list_type',
'event_list_available_only',
'event_list_filters',
'event_calendar_future_only',
'frontpage_text',
'event_info_text',
@@ -796,8 +795,6 @@ class EventSettingsSerializer(SettingsSerializer):
'cancel_allow_user_paid_refund_as_giftcard',
'cancel_allow_user_paid_require_approval',
'cancel_allow_user_paid_require_approval_fee_unknown',
'cancel_terms_paid',
'cancel_terms_unpaid',
'change_allow_user_variation',
'change_allow_user_addons',
'change_allow_user_until',
+4 -7
View File
@@ -61,7 +61,7 @@ class InlineItemVariationSerializer(I18nAwareModelSerializer):
fields = ('id', 'value', 'active', 'description',
'position', 'default_price', 'price', 'original_price', 'free_price_suggestion', 'require_approval',
'require_membership', 'require_membership_types', 'require_membership_hidden',
'checkin_attention', 'checkin_text', 'available_from', 'available_until',
'checkin_attention', 'available_from', 'available_until',
'sales_channels', 'hide_without_voucher', 'meta_data')
def __init__(self, *args, **kwargs):
@@ -85,7 +85,7 @@ class ItemVariationSerializer(I18nAwareModelSerializer):
fields = ('id', 'value', 'active', 'description',
'position', 'default_price', 'price', 'original_price', 'free_price_suggestion', 'require_approval',
'require_membership', 'require_membership_types', 'require_membership_hidden',
'checkin_attention', 'checkin_text', 'available_from', 'available_until',
'checkin_attention', 'available_from', 'available_until',
'sales_channels', 'hide_without_voucher', 'meta_data')
def __init__(self, *args, **kwargs):
@@ -237,7 +237,7 @@ class ItemSerializer(I18nAwareModelSerializer):
'default_price', 'free_price', 'free_price_suggestion', 'tax_rate', 'tax_rule', 'admission',
'personalized', 'position', 'picture', 'available_from', 'available_until',
'require_voucher', 'hide_without_voucher', 'allow_cancel', 'require_bundling',
'min_per_order', 'max_per_order', 'checkin_attention', 'checkin_text', 'has_variations', 'variations',
'min_per_order', 'max_per_order', 'checkin_attention', 'has_variations', 'variations',
'addons', 'bundles', 'original_price', 'require_approval', 'generate_tickets',
'show_quota_left', 'hidden_if_available', 'hidden_if_item_available', 'allow_waitinglist',
'issue_giftcard', 'meta_data',
@@ -440,7 +440,7 @@ class QuestionSerializer(I18nAwareModelSerializer):
class Meta:
model = Question
fields = ('id', 'question', 'type', 'required', 'items', 'options', 'position',
'ask_during_checkin', 'show_during_checkin', 'identifier', 'dependency_question', 'dependency_values',
'ask_during_checkin', 'identifier', 'dependency_question', 'dependency_values',
'hidden', 'dependency_value', 'print_on_invoice', 'help_text', 'valid_number_min',
'valid_number_max', 'valid_date_min', 'valid_date_max', 'valid_datetime_min', 'valid_datetime_max',
'valid_string_length_max', 'valid_file_portrait')
@@ -486,9 +486,6 @@ class QuestionSerializer(I18nAwareModelSerializer):
if full_data.get('ask_during_checkin') and full_data.get('type') in Question.ASK_DURING_CHECKIN_UNSUPPORTED:
raise ValidationError(_('This type of question cannot be asked during check-in.'))
if full_data.get('show_during_checkin') and full_data.get('type') in Question.SHOW_DURING_CHECKIN_UNSUPPORTED:
raise ValidationError(_('This type of question cannot be shown during check-in.'))
Question.clean_items(event, full_data.get('items'))
return data
+8 -11
View File
@@ -44,7 +44,7 @@ from pretix.api.serializers import CompatibleJSONField
from pretix.api.serializers.event import SubEventSerializer
from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.api.serializers.item import (
InlineItemVariationSerializer, ItemSerializer, QuestionSerializer,
InlineItemVariationSerializer, ItemSerializer,
)
from pretix.base.channels import get_all_sales_channels
from pretix.base.decimal import round_decimal
@@ -501,7 +501,7 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
# /events/…/checkinlists/…/positions/
# We're unable to check this on this level if we're on /checkinrpc/, in which case we rely on the view
# layer to not set pdf_data=true in the first place.
request and hasattr(request, 'eventpermset') and 'can_view_orders' not in request.eventpermset
request and hasattr(request, 'event') and 'can_view_orders' not in request.eventpermset
)
if ('pdf_data' in self.context and not self.context['pdf_data']) or pdf_data_forbidden:
self.fields.pop('pdf_data', None)
@@ -585,9 +585,6 @@ class CheckinListOrderPositionSerializer(OrderPositionSerializer):
if 'variation' in self.context['expand']:
self.fields['variation'] = InlineItemVariationSerializer(read_only=True)
if 'answers.question' in self.context['expand']:
self.fields['answers'].child.fields['question'] = QuestionSerializer(read_only=True)
class OrderPaymentTypeField(serializers.Field):
# TODO: Remove after pretix 2.2
@@ -718,7 +715,7 @@ class OrderSerializer(I18nAwareModelSerializer):
fields = (
'code', 'event', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
'checkin_attention', 'checkin_text', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
'checkin_attention', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
'url', 'customer', 'valid_if_pending'
)
read_only_fields = (
@@ -774,8 +771,8 @@ class OrderSerializer(I18nAwareModelSerializer):
def update(self, instance, validated_data):
# Even though all fields that shouldn't be edited are marked as read_only in the serializer
# (hopefully), we'll be extra careful here and be explicit about the model fields we update.
update_fields = ['comment', 'custom_followup_at', 'checkin_attention', 'checkin_text', 'email', 'locale',
'phone', 'valid_if_pending']
update_fields = ['comment', 'custom_followup_at', 'checkin_attention', 'email', 'locale', 'phone',
'valid_if_pending']
if 'invoice_address' in validated_data:
iadata = validated_data.pop('invoice_address')
@@ -1039,9 +1036,9 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
class Meta:
model = Order
fields = ('code', 'status', 'testmode', 'email', 'phone', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
'invoice_address', 'positions', 'checkin_attention', 'checkin_text', 'payment_info', 'payment_date',
'consume_carts', 'force', 'send_email', 'simulate', 'customer', 'custom_followup_at',
'require_approval', 'valid_if_pending')
'invoice_address', 'positions', 'checkin_attention', 'payment_info', 'payment_date', 'consume_carts',
'force', 'send_email', 'simulate', 'customer', 'custom_followup_at', 'require_approval',
'valid_if_pending')
def validate_payment_provider(self, pp):
if pp is None:
+2 -16
View File
@@ -152,11 +152,6 @@ class CheckinListViewSet(viewsets.ModelViewSet):
@action(detail=True, methods=['POST'], url_name='failed_checkins')
@transaction.atomic()
def failed_checkins(self, *args, **kwargs):
additional_log_data = {}
if 'debug_data' in self.request.data:
# Intentionally undocumented, might be removed again
additional_log_data['debug_data'] = self.request.data.pop('debug_data')
serializer = FailedCheckinSerializer(
data=self.request.data,
context={'event': self.request.event}
@@ -199,16 +194,14 @@ class CheckinListViewSet(viewsets.ModelViewSet):
'reason_explanation': c.error_explanation,
'datetime': c.datetime,
'type': c.type,
'list': c.list.pk,
**additional_log_data,
'list': c.list.pk
}, user=self.request.user, auth=self.request.auth)
else:
self.request.event.log_action('pretix.event.checkin.unknown', data={
'datetime': c.datetime,
'type': c.type,
'list': c.list.pk,
'barcode': c.raw_barcode,
**additional_log_data,
'barcode': c.raw_barcode
}, user=self.request.user, auth=self.request.auth)
return Response(serializer.data, status=201)
@@ -543,7 +536,6 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
'reason': Checkin.REASON_ALREADY_REDEEMED,
'reason_explanation': None,
'require_attention': False,
'checkin_texts': [],
'__warning': 'Compatibility hack active due to detected old pretixSCAN version',
}, status=400)
except: # we don't care e.g. about invalid version numbers
@@ -555,7 +547,6 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
'reason': Checkin.REASON_INVALID,
'reason_explanation': None,
'require_attention': False,
'checkin_texts': [],
'list': MiniCheckinListSerializer(checkinlists[0]).data,
}, status=404)
elif revoked_matches and force:
@@ -585,7 +576,6 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
'reason': Checkin.REASON_REVOKED,
'reason_explanation': None,
'require_attention': False,
'checkin_texts': [],
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, revoked_matches[
0].event)).data,
'list': MiniCheckinListSerializer(list_by_event[revoked_matches[0].event_id]).data,
@@ -641,7 +631,6 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
'reason': Checkin.REASON_AMBIGUOUS,
'reason_explanation': None,
'require_attention': op.require_checkin_attention,
'checkin_texts': op.checkin_texts,
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
}, status=400)
@@ -690,7 +679,6 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
return Response({
'status': 'incomplete',
'require_attention': op.require_checkin_attention,
'checkin_texts': op.checkin_texts,
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
'questions': [
QuestionSerializer(q).data for q in e.questions
@@ -721,7 +709,6 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
'reason': e.code,
'reason_explanation': e.reason,
'require_attention': op.require_checkin_attention,
'checkin_texts': op.checkin_texts,
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
}, status=400)
@@ -729,7 +716,6 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
return Response({
'status': 'ok',
'require_attention': op.require_checkin_attention,
'checkin_texts': op.checkin_texts,
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
}, status=201)
-10
View File
@@ -827,16 +827,6 @@ class EventOrderViewSet(OrderViewSetMixin, viewsets.ModelViewSet):
}
)
if 'checkin_text' in self.request.data and serializer.instance.checkin_text != self.request.data.get('checkin_text'):
serializer.instance.log_action(
'pretix.event.order.checkin_text',
user=self.request.user,
auth=self.request.auth,
data={
'new_value': self.request.data.get('checkin_text')
}
)
if 'valid_if_pending' in self.request.data and serializer.instance.valid_if_pending != self.request.data.get('valid_if_pending'):
serializer.instance.log_action(
'pretix.event.order.valid_if_pending',
+1 -1
View File
@@ -384,7 +384,7 @@ def register_default_webhook_events(sender, **kwargs):
def notify_webhooks(logentry_ids: list):
if not isinstance(logentry_ids, list):
logentry_ids = [logentry_ids]
qs = LogEntry.all.select_related('event', 'event__organizer', 'organizer_link').filter(id__in=logentry_ids)
qs = LogEntry.all.select_related('event', 'event__organizer').filter(id__in=logentry_ids)
_org, _at, webhooks = None, None, None
for logentry in qs:
if not logentry.organizer:
-3
View File
@@ -88,7 +88,6 @@ class ItemDataExporter(ListExporter):
_("Minimum amount per order"),
_("Maximum amount per order"),
_("Requires special attention"),
_("Check-in text"),
_("Original price"),
_("This product is a gift card"),
_("Require a valid membership"),
@@ -163,7 +162,6 @@ class ItemDataExporter(ListExporter):
i.min_per_order if i.min_per_order is not None else "",
i.max_per_order if i.max_per_order is not None else "",
_("Yes") if i.checkin_attention else "",
i.checkin_text or "",
v.original_price or i.original_price or "",
_("Yes") if i.issue_giftcard else "",
_("Yes") if i.require_membership or v.require_membership else "",
@@ -208,7 +206,6 @@ class ItemDataExporter(ListExporter):
i.min_per_order if i.min_per_order is not None else "",
i.max_per_order if i.max_per_order is not None else "",
_("Yes") if i.checkin_attention else "",
i.checkin_text or "",
i.original_price or "",
_("Yes") if i.issue_giftcard else "",
_("Yes") if i.require_membership else "",
-3
View File
@@ -96,7 +96,6 @@ class JSONExporter(BaseExporter):
'min_per_order': item.min_per_order,
'max_per_order': item.max_per_order,
'checkin_attention': item.checkin_attention,
'checkin_text': item.checkin_text,
'original_price': item.original_price,
'issue_giftcard': item.issue_giftcard,
'meta_data': item.meta_data,
@@ -111,7 +110,6 @@ class JSONExporter(BaseExporter):
'description': str(variation.description),
'position': variation.position,
'checkin_attention': variation.checkin_attention,
'checkin_text': variation.checkin_text,
'require_approval': variation.require_approval,
'require_membership': variation.require_membership,
'sales_channels': variation.sales_channels,
@@ -166,7 +164,6 @@ class JSONExporter(BaseExporter):
'custom_followup_at': order.custom_followup_at,
'require_approval': order.require_approval,
'checkin_attention': order.checkin_attention,
'checkin_text': order.checkin_text,
'sales_channel': order.sales_channel,
'expires': order.expires,
'datetime': order.datetime,
+11 -13
View File
@@ -275,7 +275,6 @@ class OrderListExporter(MultiSheetListExporter):
headers.append(_('Invoice numbers'))
headers.append(_('Sales channel'))
headers.append(_('Requires special attention'))
headers.append(_('Check-in text'))
headers.append(_('Comment'))
headers.append(_('Follow-up date'))
headers.append(_('Positions'))
@@ -333,7 +332,7 @@ class OrderListExporter(MultiSheetListExporter):
self.event_object_cache[order.event_id].slug,
order.code,
order.total,
order.get_extended_status_display(),
order.get_status_display(),
order.email,
str(order.phone) if order.phone else '',
order.datetime.astimezone(tz).strftime('%Y-%m-%d'),
@@ -385,7 +384,6 @@ class OrderListExporter(MultiSheetListExporter):
row.append(order.invoice_numbers)
row.append(order.sales_channel)
row.append(_('Yes') if order.checkin_attention else _('No'))
row.append(order.checkin_text or "")
row.append(order.comment or "")
row.append(order.custom_followup_at.strftime("%Y-%m-%d") if order.custom_followup_at else "")
row.append(order.pcnt)
@@ -465,7 +463,7 @@ class OrderListExporter(MultiSheetListExporter):
row = [
self.event_object_cache[order.event_id].slug,
order.code,
_("canceled") if op.canceled else order.get_extended_status_display(),
_("canceled") if op.canceled else order.get_status_display(),
order.email,
str(order.phone) if order.phone else '',
order.datetime.astimezone(tz).strftime('%Y-%m-%d'),
@@ -640,7 +638,7 @@ class OrderListExporter(MultiSheetListExporter):
self.event_object_cache[order.event_id].slug,
order.code,
op.positionid,
_("canceled") if op.canceled else order.get_extended_status_display(),
_("canceled") if op.canceled else order.get_status_display(),
order.email,
str(order.phone) if order.phone else '',
order.datetime.astimezone(tz).strftime('%Y-%m-%d'),
@@ -1009,20 +1007,20 @@ class PaymentListExporter(ListExporter):
if form_data.get('end_date_range'):
dt_start, dt_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), form_data['end_date_range'], self.timezone)
if dt_start:
payments = payments.filter(payment_date__gte=dt_start)
refunds = refunds.filter(execution_date__gte=dt_start)
payments = payments.filter(created__gte=dt_start)
refunds = refunds .filter(created__gte=dt_start)
if dt_end:
payments = payments.filter(payment_date__lt=dt_end)
refunds = refunds.filter(execution_date__lt=dt_end)
payments = payments.filter(created__lt=dt_end)
refunds = refunds .filter(created__lt=dt_end)
if form_data.get('start_end_date_range'):
dt_start, dt_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), form_data['start_date_range'], self.timezone)
if dt_start:
payments = payments.filter(created__gte=dt_start)
refunds = refunds.filter(created__gte=dt_start)
payments = payments.filter(payment_date__gte=dt_start)
refunds = refunds .filter(execution_date__gte=dt_start)
if dt_end:
payments = payments.filter(created__lt=dt_end)
refunds = refunds.filter(created__lt=dt_end)
payments = payments.filter(payment_date__lt=dt_end)
refunds = refunds.filter(execution_date__lt=dt_end)
objs = sorted(list(payments) + list(refunds), key=lambda o: o.created)
@@ -104,7 +104,7 @@ class Command(BaseCommand):
with language(locale), override(timezone):
for receiver, response in signal_result:
if not response:
continue
return None
ex = response(e, o, report_status)
if ex.identifier == options['export_provider']:
params = json.loads(options.get('parameters') or '{}')
@@ -1,52 +0,0 @@
# Generated by Django 4.2.4 on 2023-10-31 10:08
import i18nfield.fields
from django.db import migrations, models
import pretix.helpers.json
def convert_allowed_values(apps, schema_editor):
EventMetaProperty = apps.get_model('pretixbase', 'EventMetaProperty')
for emp in EventMetaProperty.objects.filter(allowed_values__isnull=False):
emp.choices = [
{"key": _v.strip(), "label": {"en": _v.strip()}}
for _v in emp.allowed_values.splitlines()
]
emp.save(update_fields=["choices"])
class Migration(migrations.Migration):
dependencies = [
("pretixbase", "0249_hidden_if_item_available"),
]
operations = [
migrations.AddField(
model_name="eventmetaproperty",
name="filter_public",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="eventmetaproperty",
name="public_label",
field=i18nfield.fields.I18nCharField(null=True),
),
migrations.AddField(
model_name="eventmetaproperty",
name="position",
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name="eventmetaproperty",
name="choices",
field=models.JSONField(null=True, encoder=pretix.helpers.json.CustomJSONEncoder),
),
migrations.RunPython(
convert_allowed_values,
migrations.RunPython.noop
),
migrations.RemoveField(
model_name="eventmetaproperty",
name="allowed_values",
),
]
@@ -1,17 +0,0 @@
# Generated by Django 4.2.4 on 2023-11-13 16:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretixbase", "0250_eventmetaproperty_filter_public"),
]
operations = [
migrations.AddField(
model_name="order",
name="invoice_dirty",
field=models.BooleanField(default=False),
),
]
@@ -1,56 +0,0 @@
# Generated by Django 4.2.4 on 2023-11-20 12:38
import django.db.models.deletion
from django.db import migrations, models
from django.db.models import F, OuterRef, Subquery
def backfill_organizer(apps, schema_editor):
LogEntry = apps.get_model("pretixbase", "LogEntry")
Event = apps.get_model("pretixbase", "Event")
ContentType = apps.get_model("contenttypes", "ContentType")
LogEntry.objects.filter(
organizer_link__isnull=True, event__isnull=False
).update(organizer_link_id=Subquery(
Event.objects.filter(pk=OuterRef('event_id')).values('organizer_id'),
)
)
for ct in ContentType.objects.all():
try:
model = apps.get_model(ct.app_label, ct.model)
except LookupError:
continue
if "organizer" in model._meta.fields:
LogEntry.objects.filter(
organizer_link__isnull=True, event__isnull=True, content_type=ct,
).update(
organizer_link_id=Subquery(model.objects.filter(pk=OuterRef('object_id')).values('organizer_id'))
)
elif "event" in model._meta.fields:
LogEntry.objects.filter(
organizer_link__isnull=True, event__isnull=True, content_type=ct,
).update(
organizer_link_id=Subquery(model.objects.filter(pk=OuterRef('object_id')).values('event__organizer_id'))
)
class Migration(migrations.Migration):
dependencies = [
("pretixbase", "0251_order_invoice_dirty"),
]
operations = [
migrations.AddField(
model_name="logentry",
name="organizer_link",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="pretixbase.organizer",
),
),
migrations.RunPython(
backfill_organizer,
)
]
@@ -1,32 +0,0 @@
# Generated by Django 4.2.4 on 2023-09-06 09:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretixbase", "0252_logentry_organizer"),
]
operations = [
migrations.AddField(
model_name="item",
name="checkin_text",
field=models.TextField(null=True),
),
migrations.AddField(
model_name="itemvariation",
name="checkin_text",
field=models.TextField(null=True),
),
migrations.AddField(
model_name="order",
name="checkin_text",
field=models.TextField(null=True),
),
migrations.AddField(
model_name="question",
name="show_during_checkin",
field=models.BooleanField(default=False),
),
]
+3 -12
View File
@@ -84,21 +84,13 @@ class LoggingMixin:
from .devices import Device
from .event import Event
from .log import LogEntry
from .organizer import Organizer, TeamAPIToken
from .organizer import TeamAPIToken
event = None
organizer_id = None
if isinstance(self, Organizer):
organizer_id = self.pk
elif isinstance(self, Event):
if isinstance(self, Event):
event = self
organizer_id = self.organizer_id
elif hasattr(self, 'event'):
event = self.event
organizer_id = self.event.organizer_id
elif hasattr(self, 'organizer_id'):
organizer_id = self.organizer_id
if user and not user.is_authenticated:
user = None
@@ -114,8 +106,7 @@ class LoggingMixin:
elif isinstance(api_token, TeamAPIToken):
kwargs['api_token'] = api_token
logentry = LogEntry(content_object=self, user=user, action_type=action, event=event,
organizer_link_id=organizer_id, **kwargs)
logentry = LogEntry(content_object=self, user=user, action_type=action, event=event, **kwargs)
if isinstance(data, dict):
sensitivekeys = ['password', 'secret', 'api_key']
-2
View File
@@ -352,7 +352,6 @@ class Checkin(models.Model):
REASON_AMBIGUOUS = 'ambiguous'
REASON_ERROR = 'error'
REASON_BLOCKED = 'blocked'
REASON_UNAPPROVED = 'unapproved'
REASON_INVALID_TIME = 'invalid_time'
REASONS = (
(REASON_CANCELED, _('Order canceled')),
@@ -366,7 +365,6 @@ class Checkin(models.Model):
(REASON_AMBIGUOUS, _('Ticket code is ambiguous on list')),
(REASON_ERROR, _('Server error')),
(REASON_BLOCKED, _('Ticket blocked')),
(REASON_UNAPPROVED, _('Order not approved')),
(REASON_INVALID_TIME, _('Ticket not valid at this time')),
)
+1 -6
View File
@@ -424,10 +424,5 @@ class Discount(LoggedModel):
break
for g in candidate_groups:
self._apply_min_count(
positions,
[idx for idx in g if idx in condition_candidates],
[idx for idx in g if idx in benefit_candidates],
result
)
self._apply_min_count(positions, g, g, result)
return result
+10 -31
View File
@@ -20,6 +20,7 @@
# <https://www.gnu.org/licenses/>.
#
import logging
# This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of
# the Apache License 2.0 can be obtained at <http://www.apache.org/licenses/LICENSE-2.0>.
#
@@ -32,7 +33,6 @@
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
import logging
import os
import string
import uuid
@@ -71,7 +71,7 @@ from pretix.base.validators import EventSlugBanlistValidator
from pretix.helpers.database import GroupConcat
from pretix.helpers.daterange import daterange
from pretix.helpers.hierarkey import clean_filename
from pretix.helpers.json import CustomJSONEncoder, safe_string
from pretix.helpers.json import safe_string
from pretix.helpers.thumb import get_thumbnail
from ..settings import settings_hierarkey
@@ -798,11 +798,6 @@ class Event(EventMixin, LoggedModel):
self.save()
self.log_action('pretix.object.cloned', data={'source': other.slug, 'source_id': other.pk})
for emv in EventMetaValue.objects.filter(event=other):
emv.pk = None
emv.event = self
emv.save(force_insert=True)
for fl in EventFooterLink.objects.filter(event=other):
fl.pk = None
fl.event = self
@@ -1006,7 +1001,6 @@ class Event(EventMixin, LoggedModel):
'presale_widget_css_file',
'presale_widget_css_checksum',
)
settings_to_save = []
for s in other.settings._objects.all():
if s.key in skip_settings:
continue
@@ -1024,17 +1018,16 @@ class Event(EventMixin, LoggedModel):
)
newname = default_storage.save(fname, fi)
s.value = 'file://' + newname
settings_to_save.append(s)
s.save()
elif s.key == 'tax_rate_default':
try:
if int(s.value) in tax_map:
s.value = tax_map.get(int(s.value)).pk
settings_to_save.append(s)
s.save()
except ValueError:
pass
else:
settings_to_save.append(s)
other.settings._objects.bulk_create(settings_to_save)
s.save()
self.settings.flush()
event_copy_data.send(
@@ -1652,40 +1645,26 @@ class EventMetaProperty(LoggedModel):
help_text=_("If checked, an event can only be taken live if the property is set. In event series, its always "
"optional to set a value for individual dates")
)
choices = models.JSONField(
allowed_values = models.TextField(
null=True, blank=True,
encoder=CustomJSONEncoder,
verbose_name=_("Valid values"),
)
filter_public = models.BooleanField(
default=False, verbose_name=_("Show filter option to customers"),
help_text=_("This field will be shown to filter events in the public event list and calendar.")
)
public_label = I18nCharField(
verbose_name=_("Public name"),
null=True, blank=True,
help_text=_("If you keep this empty, any value is allowed. Otherwise, enter one possible value per line.")
)
filter_allowed = models.BooleanField(
default=True, verbose_name=_("Can be used for filtering"),
help_text=_("This field will be shown to filter events or reports in the backend, and it can also be used "
"for hidden filter parameters in the frontend (e.g. using the widget).")
)
position = models.IntegerField(
default=0
)
def full_clean(self, exclude=None, validate_unique=True):
super().full_clean(exclude, validate_unique)
if self.default and self.required:
raise ValidationError(_("A property can either be required or have a default value, not both."))
if self.default and self.allowed_values and self.default not in self.allowed_values.splitlines():
raise ValidationError(_("You cannot set a default value that is not a valid value."))
class Meta:
ordering = ("position", "name",)
@property
def choice_keys(self):
if self.choices:
return [v["key"] for v in self.choices]
ordering = ("name",)
class EventMetaValue(LoggedModel):
-20
View File
@@ -336,8 +336,6 @@ class Item(LoggedModel):
:type min_per_order: int
:param checkin_attention: Requires special attention at check-in
:type checkin_attention: bool
:param checkin_text: Additional text to show at check-in
:type checkin_text: bool
:param original_price: The item's "original" price. Will not be used for any calculations, will just be shown.
:type original_price: decimal.Decimal
:param require_approval: If set to ``True``, orders containing this product can only be processed and paid after approved by an administrator
@@ -568,11 +566,6 @@ class Item(LoggedModel):
'attention. You can use this for example for student tickets to indicate to the person at '
'check-in that the student ID card still needs to be checked.')
)
checkin_text = models.TextField(
verbose_name=_('Check-in text'),
null=True, blank=True,
help_text=_('This text will be shown by the check-in app if a ticket of this type is scanned.')
)
original_price = models.DecimalField(
verbose_name=_('Original price'),
blank=True, null=True,
@@ -1103,11 +1096,6 @@ class ItemVariation(models.Model):
'attention. You can use this for example for student tickets to indicate to the person at '
'check-in that the student ID card still needs to be checked.')
)
checkin_text = models.TextField(
verbose_name=_('Check-in text'),
null=True, blank=True,
help_text=_('This text will be shown by the check-in app if a ticket of this type is scanned.')
)
objects = ScopedManager(organizer='item__event__organizer')
@@ -1462,8 +1450,6 @@ class Question(LoggedModel):
:param items: A set of ``Items`` objects that this question should be applied to
:param ask_during_checkin: Whether to ask this question during check-in instead of during check-out.
:type ask_during_checkin: bool
:param show_during_checkin: Whether to show the answer to this question during check-in.
:type show_during_checkin: bool
:param hidden: Whether to only show the question in the backend
:type hidden: bool
:param identifier: An arbitrary, internal identifier
@@ -1501,7 +1487,6 @@ class Question(LoggedModel):
)
UNLOCALIZED_TYPES = [TYPE_DATE, TYPE_TIME, TYPE_DATETIME]
ASK_DURING_CHECKIN_UNSUPPORTED = []
SHOW_DURING_CHECKIN_UNSUPPORTED = [TYPE_FILE]
event = models.ForeignKey(
Event,
@@ -1553,11 +1538,6 @@ class Question(LoggedModel):
help_text=_('Not supported by all check-in apps for all question types.'),
default=False
)
show_during_checkin = models.BooleanField(
verbose_name=_('Show answer during check-in'),
help_text=_('Not supported by all check-in apps for all question types.'),
default=False
)
hidden = models.BooleanField(
verbose_name=_('Hidden question'),
help_text=_('This question will only show up in the backend.'),
+1 -4
View File
@@ -78,7 +78,6 @@ class LogEntry(models.Model):
device = models.ForeignKey('Device', null=True, blank=True, on_delete=models.PROTECT)
oauth_application = models.ForeignKey('pretixapi.OAuthApplication', null=True, blank=True, on_delete=models.PROTECT)
event = models.ForeignKey('Event', null=True, blank=True, on_delete=models.SET_NULL)
organizer_link = models.ForeignKey('Organizer', null=True, blank=True, on_delete=models.PROTECT)
action_type = models.CharField(max_length=255)
data = models.TextField(default='{}')
visible = models.BooleanField(default=True)
@@ -127,9 +126,7 @@ class LogEntry(models.Model):
def organizer(self):
from .organizer import Organizer
if self.organizer_link:
return self.organizer_link
elif self.event:
if self.event:
return self.event.organizer
elif hasattr(self.content_object, 'event'):
return self.content_object.event.organizer
+2 -56
View File
@@ -244,11 +244,6 @@ class Order(LockModel, LoggedModel):
'special attention. This will not show any details or custom message, so you need to brief your '
'check-in staff how to handle these cases.')
)
checkin_text = models.TextField(
verbose_name=_('Check-in text'),
null=True, blank=True,
help_text=_('This text will be shown by the check-in app if a ticket of this order is scanned.')
)
expiry_reminder_sent = models.BooleanField(
default=False
)
@@ -271,10 +266,6 @@ class Order(LockModel, LoggedModel):
default=False,
verbose_name=_('E-mail address verified')
)
invoice_dirty = models.BooleanField(
# Invoice needs to be re-issued when the order is paid again
default=False,
)
objects = ScopedManager(organizer='event__organizer')
@@ -334,18 +325,6 @@ class Order(LockModel, LoggedModel):
def email_confirm_hash(self):
return hashlib.sha256(settings.SECRET_KEY.encode() + self.secret.encode()).hexdigest()[:9]
def get_extended_status_display(self):
# Changes in this method should to be replicated in pretixcontrol/orders/fragment_order_status.html
# and pretixpresale/event/fragment_order_status.html
if self.status == Order.STATUS_PENDING:
if self.require_approval:
return _("approval pending")
elif self.valid_if_pending:
return pgettext_lazy("order state", "pending (confirmed)")
elif self.status == Order.STATUS_PAID and self.count_positions == 0:
return _("canceled (paid fee)")
return self.get_status_display()
@property
def fees(self):
"""
@@ -1292,21 +1271,6 @@ class QuestionAnswer(models.Model):
return self.file.name.split('.', 1)[-1]
def __str__(self):
return self.to_string(use_cached=True)
def to_string_i18n(self):
return self.to_string(use_cached=False)
def to_string(self, use_cached=True):
"""
Render this answer as a string.
:param use_cached: If ``True`` (default), choice and multiple choice questions will show their cached
value, i.e. the value of the selected options at the time of saving and in the language
the answer was saved in. If ``False``, the values will instead be loaded from the
database, yielding current and translated values of the options. However, additional database
queries might be required.
"""
if self.question.type == Question.TYPE_BOOLEAN and self.answer == "True":
return str(_("Yes"))
elif self.question.type == Question.TYPE_BOOLEAN and self.answer == "False":
@@ -1341,8 +1305,6 @@ class QuestionAnswer(models.Model):
return PhoneNumber.from_string(self.answer).as_international
except NumberParseException:
return self.answer
elif self.question.type in (Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE) and self.answer and not use_cached:
return ", ".join(str(o.answer) for o in self.options.all())
else:
return self.answer
@@ -1844,7 +1806,7 @@ class OrderPayment(models.Model):
def _mark_order_paid(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text='',
ignore_date=False, lock=True, payment_refund_sum=0, allow_generate_invoice=True):
from pretix.base.services.invoices import (
generate_cancellation, generate_invoice, invoice_qualified,
generate_invoice, invoice_qualified,
)
from pretix.base.services.locking import LOCK_TRUST_WINDOW
@@ -1862,14 +1824,9 @@ class OrderPayment(models.Model):
cancellations = self.order.invoices.filter(is_cancellation=True).count()
gen_invoice = (
(invoices == 0 and self.order.event.settings.get('invoice_generate') in ('True', 'paid')) or
0 < invoices <= cancellations or
self.order.invoice_dirty
0 < invoices <= cancellations
)
if gen_invoice:
if invoices:
last_i = self.order.invoices.filter(is_cancellation=False).last()
if not last_i.canceled:
generate_cancellation(last_i)
invoice = generate_invoice(
self.order,
trigger_pdf=not send_mail or not self.order.event.settings.invoice_email_attachment
@@ -2430,17 +2387,6 @@ class OrderPosition(AbstractPosition):
return True
return False
@cached_property
def checkin_texts(self):
texts = []
if self.order.checkin_text:
texts.append(self.order.checkin_text)
if self.variation_id and self.variation.checkin_text:
texts.append(self.variation.checkin_text)
if self.item.checkin_text:
texts.append(self.item.checkin_text)
return texts
@property
def checkins(self):
"""
+1 -1
View File
@@ -567,7 +567,7 @@ def variables_from_questions(sender, *args, **kwargs):
if not a:
return ""
else:
return a.to_string_i18n()
return str(a)
d = {}
for q in sender.questions.all():
+36 -82
View File
@@ -39,7 +39,7 @@ BASE_CHOICES = (
('presale_end', _('Presale end')),
)
RelativeDate = namedtuple('RelativeDate', ['days', 'minutes', 'time', 'is_after', 'base_date_name'], defaults=(0, None, None, False, 'date_from'))
RelativeDate = namedtuple('RelativeDate', ['days_before', 'minutes_before', 'time', 'base_date_name'])
class RelativeDateWrapper:
@@ -64,7 +64,7 @@ class RelativeDateWrapper:
elif isinstance(self.data, datetime.date):
return self.data
else:
if self.data.minutes is not None:
if self.data.minutes_before is not None:
raise ValueError('A minute-based relative datetime can not be used as a date')
tz = ZoneInfo(event.settings.timezone)
@@ -77,10 +77,7 @@ class RelativeDateWrapper:
else:
base_date = getattr(event, self.data.base_date_name) or event.date_from
if self.data.is_after:
new_date = base_date.astimezone(tz) + datetime.timedelta(days=self.data.days)
else:
new_date = base_date.astimezone(tz) - datetime.timedelta(days=self.data.days)
new_date = base_date.astimezone(tz) - datetime.timedelta(days=self.data.days_before)
return new_date.date()
def datetime(self, event) -> datetime.datetime:
@@ -99,16 +96,10 @@ class RelativeDateWrapper:
else:
base_date = getattr(event, self.data.base_date_name) or event.date_from
if self.data.minutes is not None:
if self.data.is_after:
return base_date.astimezone(tz) + datetime.timedelta(minutes=self.data.minutes)
else:
return base_date.astimezone(tz) - datetime.timedelta(minutes=self.data.minutes)
if self.data.minutes_before is not None:
return base_date.astimezone(tz) - datetime.timedelta(minutes=self.data.minutes_before)
else:
if self.data.is_after:
new_date = (base_date.astimezone(tz) + datetime.timedelta(days=self.data.days)).astimezone(tz)
else:
new_date = (base_date.astimezone(tz) - datetime.timedelta(days=self.data.days)).astimezone(tz)
new_date = (base_date.astimezone(tz) - datetime.timedelta(days=self.data.days_before)).astimezone(tz)
if self.data.time:
new_date = new_date.replace(
hour=self.data.time.hour,
@@ -122,17 +113,15 @@ class RelativeDateWrapper:
if isinstance(self.data, (datetime.datetime, datetime.date)):
return self.data.isoformat()
else:
if self.data.minutes is not None:
return 'RELDATE/minutes/{}/{}/{}'.format( #
self.data.minutes,
self.data.base_date_name,
'after' if self.data.is_after else '',
if self.data.minutes_before is not None:
return 'RELDATE/minutes/{}/{}/'.format( #
self.data.minutes_before,
self.data.base_date_name
)
return 'RELDATE/{}/{}/{}/{}'.format( #
self.data.days,
return 'RELDATE/{}/{}/{}/'.format( #
self.data.days_before,
self.data.time.strftime('%H:%M:%S') if self.data.time else '-',
self.data.base_date_name,
'after' if self.data.is_after else '',
self.data.base_date_name
)
@classmethod
@@ -141,11 +130,10 @@ class RelativeDateWrapper:
parts = input.split('/')
if parts[1] == 'minutes':
data = RelativeDate(
days=0,
minutes=int(parts[2]),
days_before=0,
minutes_before=int(parts[2]),
base_date_name=parts[3],
time=None,
is_after=len(parts) > 4 and parts[4] == "after",
time=None
)
else:
if parts[2] == '-':
@@ -155,19 +143,17 @@ class RelativeDateWrapper:
time = datetime.time(hour=int(timeparts[0]), minute=int(timeparts[1]), second=int(timeparts[2]))
try:
data = RelativeDate(
days=int(parts[1] or 0),
days_before=int(parts[1] or 0),
base_date_name=parts[3],
time=time,
minutes=None,
is_after=len(parts) > 4 and parts[4] == "after",
minutes_before=None
)
except ValueError:
data = RelativeDate(
days=0,
days_before=0,
base_date_name=parts[3],
time=time,
minutes=None,
is_after=len(parts) > 4 and parts[4] == "after",
minutes_before=None
)
if data.base_date_name not in [k[0] for k in BASE_CHOICES]:
raise ValueError('{} is not a valid base date'.format(data.base_date_name))
@@ -179,30 +165,20 @@ class RelativeDateWrapper:
return len(self.to_string())
BEFORE_AFTER_CHOICE = (
('before', _('before')),
('after', _('after')),
)
class RelativeDateTimeWidget(forms.MultiWidget):
template_name = 'pretixbase/forms/widgets/reldatetime.html'
def __init__(self, *args, **kwargs):
self.status_choices = kwargs.pop('status_choices')
base_choices = kwargs.pop('base_choices')
widgets = (
forms.RadioSelect(choices=self.status_choices),
forms.DateTimeInput(
attrs={'class': 'datetimepicker'}
),
forms.NumberInput(),
forms.Select(choices=base_choices),
forms.Select(choices=kwargs.pop('base_choices')),
forms.TimeInput(attrs={'placeholder': _('Time'), 'class': 'timepickerfield'}),
forms.NumberInput(),
forms.Select(choices=base_choices),
forms.Select(choices=BEFORE_AFTER_CHOICE),
forms.Select(choices=BEFORE_AFTER_CHOICE),
)
super().__init__(widgets=widgets, *args, **kwargs)
@@ -210,14 +186,12 @@ class RelativeDateTimeWidget(forms.MultiWidget):
if isinstance(value, str):
value = RelativeDateWrapper.from_string(value)
if not value:
return ['unset', None, 1, 'date_from', None, 0, "date_from", "before", "before"]
return ['unset', None, 1, 'date_from', None, 0]
elif isinstance(value.data, (datetime.datetime, datetime.date)):
return ['absolute', value.data, 1, 'date_from', None, 0, "date_from", "before", "before"]
elif value.data.minutes is not None:
return ['relative_minutes', None, None, value.data.base_date_name, None, value.data.minutes, value.data.base_date_name,
"after" if value.data.is_after else "before", "after" if value.data.is_after else "before"]
return ['relative', None, value.data.days, value.data.base_date_name, value.data.time, 0, value.data.base_date_name,
"after" if value.data.is_after else "before", "after" if value.data.is_after else "before"]
return ['absolute', value.data, 1, 'date_from', None, 0]
elif value.data.minutes_before is not None:
return ['relative_minutes', None, None, value.data.base_date_name, None, value.data.minutes_before]
return ['relative', None, value.data.days_before, value.data.base_date_name, value.data.time, 0]
def get_context(self, name, value, attrs):
ctx = super().get_context(name, value, attrs)
@@ -260,18 +234,6 @@ class RelativeDateTimeField(forms.MultiValueField):
forms.IntegerField(
required=False
),
forms.ChoiceField(
choices=choices,
required=False
),
forms.ChoiceField(
choices=BEFORE_AFTER_CHOICE,
required=False
),
forms.ChoiceField(
choices=BEFORE_AFTER_CHOICE,
required=False
),
)
if 'widget' not in kwargs:
kwargs['widget'] = RelativeDateTimeWidget(status_choices=status_choices, base_choices=choices)
@@ -295,19 +257,17 @@ class RelativeDateTimeField(forms.MultiValueField):
return None
elif data_list[0] == 'relative_minutes':
return RelativeDateWrapper(RelativeDate(
days=0,
days_before=0,
base_date_name=data_list[3],
time=None,
minutes=data_list[5],
is_after=data_list[7] == "after",
minutes_before=data_list[5]
))
else:
return RelativeDateWrapper(RelativeDate(
days=data_list[2],
base_date_name=data_list[6],
days_before=data_list[2],
base_date_name=data_list[3],
time=data_list[4],
minutes=None,
is_after=data_list[8] == "after",
minutes_before=None
))
def has_changed(self, initial, data):
@@ -338,7 +298,6 @@ class RelativeDateWidget(RelativeDateTimeWidget):
),
forms.NumberInput(),
forms.Select(choices=kwargs.pop('base_choices')),
forms.Select(choices=BEFORE_AFTER_CHOICE),
)
forms.MultiWidget.__init__(self, widgets=widgets, *args, **kwargs)
@@ -346,10 +305,10 @@ class RelativeDateWidget(RelativeDateTimeWidget):
if isinstance(value, str):
value = RelativeDateWrapper.from_string(value)
if not value:
return ['unset', None, 1, 'date_from', 'before']
return ['unset', None, 1, 'date_from']
elif isinstance(value.data, (datetime.datetime, datetime.date)):
return ['absolute', value.data, 1, 'date_from', 'before']
return ['relative', None, value.data.days, value.data.base_date_name, "after" if value.data.is_after else "before"]
return ['absolute', value.data, 1, 'date_from']
return ['relative', None, value.data.days_before, value.data.base_date_name]
class RelativeDateField(RelativeDateTimeField):
@@ -376,10 +335,6 @@ class RelativeDateField(RelativeDateTimeField):
choices=BASE_CHOICES,
required=False
),
forms.ChoiceField(
choices=BEFORE_AFTER_CHOICE,
required=False
),
)
if 'widget' not in kwargs:
kwargs['widget'] = RelativeDateWidget(status_choices=status_choices, base_choices=BASE_CHOICES)
@@ -396,10 +351,9 @@ class RelativeDateField(RelativeDateTimeField):
return None
else:
return RelativeDateWrapper(RelativeDate(
days=data_list[2],
days_before=data_list[2],
base_date_name=data_list[3],
time=None, minutes=None,
is_after=data_list[4] == "after"
time=None, minutes_before=None
))
def clean(self, value):
+8 -9
View File
@@ -874,15 +874,6 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
'blocked'
)
if op.order.status == Order.STATUS_PENDING and op.order.require_approval:
if force:
force_used = True
else:
raise CheckInError(
_('This order is not yet approved.'),
'unapproved',
)
if type != Checkin.TYPE_EXIT and op.valid_from and op.valid_from > dt:
if force:
force_used = True
@@ -950,6 +941,14 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
'product'
)
if op.order.status != Order.STATUS_PAID and op.order.require_approval:
if force:
force_used = True
else:
raise CheckInError(
_('This order is not yet approved.'),
'unpaid'
)
elif op.order.status != Order.STATUS_PAID and not op.order.valid_if_pending and not (
ignore_unpaid and clist.include_pending and op.order.status == Order.STATUS_PENDING
):
+2 -6
View File
@@ -199,7 +199,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
positions = list(
invoice.order.positions.select_related('addon_to', 'item', 'tax_rule', 'subevent', 'variation').annotate(
addon_c=Count('addons')
).prefetch_related('answers', 'answers__options', 'answers__question').order_by('positionid', 'id')
).prefetch_related('answers', 'answers__question').order_by('positionid', 'id')
)
reverse_charge = False
@@ -247,7 +247,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
desc += "<br />{}{} {}".format(
answ.question.question,
"" if str(answ.question.question).endswith("?") else ":",
answ.to_string_i18n()
str(answ)
)
if invoice.event.has_subevents:
@@ -395,10 +395,6 @@ def generate_invoice(order: Order, trigger_pdf=True):
if order.status == Order.STATUS_CANCELED:
generate_cancellation(invoice, trigger_pdf)
if order.invoice_dirty:
order.invoice_dirty = False
order.save(update_fields=['invoice_dirty'])
return invoice
+1 -2
View File
@@ -100,8 +100,7 @@ def lock_objects(objects, *, shared_lock_objects=None, replace_exclusive_with_sh
if 'postgresql' in settings.DATABASES['default']['ENGINE']:
shared_keys = set(pg_lock_key(obj) for obj in shared_lock_objects) if shared_lock_objects else set()
exclusive_keys = set(pg_lock_key(obj) for obj in objects)
if replace_exclusive_with_shared_when_exclusive_are_more_than and shared_keys and \
len(exclusive_keys) > replace_exclusive_with_shared_when_exclusive_are_more_than:
if replace_exclusive_with_shared_when_exclusive_are_more_than and len(exclusive_keys) > replace_exclusive_with_shared_when_exclusive_are_more_than:
exclusive_keys = shared_keys
keys = sorted(list(shared_keys | exclusive_keys))
calls = ", ".join([
@@ -30,7 +30,6 @@ from pretix.base.models import LogEntry, NotificationSetting, User
from pretix.base.notifications import Notification, get_all_notification_types
from pretix.base.services.mail import mail_send_task
from pretix.base.services.tasks import ProfiledTask, TransactionAwareTask
from pretix.base.signals import notification
from pretix.celery_app import app
from pretix.helpers.urls import build_absolute_uri
@@ -91,8 +90,6 @@ def notify(logentry_ids: list):
if enabled and um not in notify_specific:
send_notification.apply_async(args=(logentry.id, notification_type.action_type, user.pk, method))
notification.send(logentry.event, logentry_id=logentry.id, notification_type=notification_type.action_type)
@app.task(base=ProfiledTask, acks_late=True, max_retries=9, default_retry_delay=900)
def send_notification(logentry_id: int, action_type: str, user_id: int, method: str):
+9 -34
View File
@@ -1380,9 +1380,7 @@ def send_download_reminders(sender, **kwargs):
download_reminder_sent=False,
datetime__lte=now() - timedelta(hours=2),
first_date__gte=today,
).only(
'pk', 'event_id', 'sales_channel', 'datetime',
).order_by('event_id')
).only('pk', 'event_id', 'sales_channel').order_by('event_id')
event_id = None
days = None
event = None
@@ -2619,37 +2617,14 @@ class OrderChangeManager:
def _reissue_invoice(self):
i = self.order.invoices.filter(is_cancellation=False).last()
if self.reissue_invoice and self._invoice_dirty:
order_now_qualified = invoice_qualified(self.order)
invoice_should_be_generated_now = (
self.event.settings.invoice_generate == "True" or (
self.event.settings.invoice_generate == "paid" and
self.open_payment is not None and
self.open_payment.payment_provider.requires_invoice_immediately
) or (
self.event.settings.invoice_generate == "paid" and
self.order.status == Order.STATUS_PAID
) or (
# Backwards-compatible behaviour
self.event.settings.invoice_generate not in ("True", "paid") and
i and
not i.canceled
)
)
invoice_should_be_generated_later = not invoice_should_be_generated_now and (
self.event.settings.invoice_generate in ("True", "paid")
)
if order_now_qualified:
if invoice_should_be_generated_now:
if i and not i.canceled:
self._invoices.append(generate_cancellation(i))
self._invoices.append(generate_invoice(self.order))
elif invoice_should_be_generated_later:
self.order.invoice_dirty = True
self.order.save(update_fields=["invoice_dirty"])
else:
if i and not i.canceled:
self._invoices.append(generate_cancellation(i))
if i and not i.refered.exists():
self._invoices.append(generate_cancellation(i))
if invoice_qualified(self.order) and \
(i or
self.event.settings.invoice_generate == 'True' or (
self.open_payment is not None and self.event.settings.invoice_generate == 'paid' and
self.open_payment.payment_provider.requires_invoice_immediately)):
self._invoices.append(generate_invoice(self.order))
def _check_complete_cancel(self):
current = self.order.positions.count()
-36
View File
@@ -1606,16 +1606,6 @@ DEFAULTS = {
help_text=_('If your event series has more than 50 dates in the future, only the month or week calendar can be used.')
),
},
'event_list_filters': {
'default': 'True',
'type': bool,
'form_class': forms.BooleanField,
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Show filter options for calendar or list view"),
help_text=_("You can set up possible filters as meta properties in your organizer settings.")
)
},
'event_list_available_only': {
'default': 'False',
'type': bool,
@@ -1936,32 +1926,6 @@ DEFAULTS = {
label=_("Do not allow cancellations after"),
)
},
'cancel_terms_paid': {
'default': None,
'type': LazyI18nString,
'serializer_class': I18nField,
'form_class': I18nFormField,
'form_kwargs': dict(
label=_("Terms of cancellation"),
widget=I18nTextarea,
widget_kwargs={'attrs': {'rows': '2'}},
help_text=_("This text will be shown when cancellation is allowed for a paid order. Leave empty if you "
"want pretix to automatically generate the terms of cancellation based on your settings.")
)
},
'cancel_terms_unpaid': {
'default': None,
'type': LazyI18nString,
'serializer_class': I18nField,
'form_class': I18nFormField,
'form_kwargs': dict(
label=_("Terms of cancellation"),
widget=I18nTextarea,
widget_kwargs={'attrs': {'rows': '2'}},
help_text=_("This text will be shown when cancellation is allowed for an unpaid or free order. Leave empty "
"if you want pretix to automatically generate the terms of cancellation based on your settings.")
)
},
'contact_mail': {
'default': None,
'type': str,
-9
View File
@@ -279,15 +279,6 @@ however for this signal, the ``sender`` **may also be None** to allow creating t
notification settings!
"""
notification = EventPluginSignal()
"""
Arguments: ``logentry_id``, ``notification_type``
This signal is sent out when a notification is sent.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
register_sales_channels = django.dispatch.Signal()
"""
This signal is sent out to get all known sales channels types. Receivers should return an
@@ -12,8 +12,7 @@
{% include widget.subwidgets.1.template_name with widget=widget.subwidgets.1 %}
{% elif selopt.value == "relative" %}
{% include widget.subwidgets.2.template_name with widget=widget.subwidgets.2 %}
{% trans "days" %}
{% include widget.subwidgets.4.template_name with widget=widget.subwidgets.4 %}
{% trans "days before" %}
{% include widget.subwidgets.3.template_name with widget=widget.subwidgets.3 %}
{% endif %}
</div>
@@ -12,14 +12,12 @@
{% include widget.subwidgets.1.template_name with widget=widget.subwidgets.1 %}
{% elif selopt.value == "relative_minutes" %}
{% include widget.subwidgets.5.template_name with widget=widget.subwidgets.5 %}
{% trans "minutes" %}
{% include widget.subwidgets.7.template_name with widget=widget.subwidgets.7 %}
{% trans "minutes before" %}
{% include widget.subwidgets.3.template_name with widget=widget.subwidgets.3 %}
{% elif selopt.value == "relative" %}
{% include widget.subwidgets.2.template_name with widget=widget.subwidgets.2 %}
{% trans "days" %}
{% include widget.subwidgets.8.template_name with widget=widget.subwidgets.8 %}
{% include widget.subwidgets.6.template_name with widget=widget.subwidgets.6 %}
{% trans "days before" %}
{% include widget.subwidgets.3.template_name with widget=widget.subwidgets.3 %}
{% trans "at" %}
{% include widget.subwidgets.4.template_name with widget=widget.subwidgets.4 %}
{% endif %}
+14 -19
View File
@@ -21,7 +21,6 @@
#
from decimal import ROUND_HALF_UP, Decimal
from babel import Locale, UnknownLocaleError
from babel.numbers import format_currency
from django import template
from django.conf import settings
@@ -48,29 +47,25 @@ def money_filter(value: Decimal, arg='', hide_currency=False):
places = settings.CURRENCY_PLACES.get(arg, 2)
rounded = value.quantize(Decimal('1') / 10 ** places, ROUND_HALF_UP)
if places < 2 and rounded != value:
# We display decimal places even if we shouldn't for this currency if rounding
# would make the numbers incorrect. If this branch executes, it's likely a bug in
# pretix, but we won't show wrong numbers!
if hide_currency:
return floatformat(value, 2)
else:
return '{} {}'.format(arg, floatformat(value, 2))
places = 2
if hide_currency:
return floatformat(value, places)
locale_parts = translation.get_language().split("-", 1)
locale = locale_parts[0]
if len(locale_parts) > 1 and len(locale_parts[1]) == 2:
try:
locale = Locale(locale_parts[0], locale_parts[1].upper())
except UnknownLocaleError:
pass
try:
return format_currency(value, arg, locale=locale)
if rounded != value:
# We display decimal places even if we shouldn't for this currency if rounding
# would make the numbers incorrect. If this branch executes, it's likely a bug in
# pretix, but we won't show wrong numbers!
return '{} {}'.format(
arg,
floatformat(value, 2)
)
return format_currency(value, arg, locale=translation.get_language()[:2])
except:
return '{} {}'.format(arg, floatformat(value, places))
return '{} {}'.format(
arg,
floatformat(value, places)
)
@register.filter("money_numberfield")
+2 -6
View File
@@ -316,12 +316,12 @@ class EventMetaValueForm(forms.ModelForm):
self.property = kwargs.pop('property')
self.disabled = kwargs.pop('disabled')
super().__init__(*args, **kwargs)
if self.property.choices:
if self.property.allowed_values:
self.fields['value'] = forms.ChoiceField(
label=self.property.name,
choices=[
('', _('Default ({value})').format(value=self.property.default) if self.property.default else ''),
] + [(a.strip(), a.strip()) for a in self.property.choice_keys],
] + [(a.strip(), a.strip()) for a in self.property.allowed_values.splitlines()],
)
else:
self.fields['value'].label = self.property.name
@@ -558,7 +558,6 @@ class EventSettingsForm(EventSettingsValidationMixin, SettingsForm):
'low_availability_percentage',
'event_list_type',
'event_list_available_only',
'event_list_filters',
'event_calendar_future_only',
'frontpage_text',
'event_info_text',
@@ -646,7 +645,6 @@ class EventSettingsForm(EventSettingsValidationMixin, SettingsForm):
del self.fields['frontpage_subevent_ordering']
del self.fields['event_list_type']
del self.fields['event_list_available_only']
del self.fields['event_list_filters']
del self.fields['event_calendar_future_only']
# create "virtual" fields for better UX when editing <name>_asked and <name>_required fields
@@ -730,8 +728,6 @@ class CancelSettingsForm(SettingsForm):
'cancel_allow_user_paid_refund_as_giftcard',
'cancel_allow_user_paid_require_approval',
'cancel_allow_user_paid_require_approval_fee_unknown',
'cancel_terms_paid',
'cancel_terms_unpaid',
'change_allow_user_variation',
'change_allow_user_price',
'change_allow_user_until',
-14
View File
@@ -133,14 +133,6 @@ class QuestionForm(I18nModelForm):
return val
def clean_show_during_checkin(self):
val = self.cleaned_data.get('show_during_checkin')
if val and self.cleaned_data.get('type') in Question.SHOW_DURING_CHECKIN_UNSUPPORTED:
raise ValidationError(_('This type of question cannot be shown during check-in.'))
return val
def clean_identifier(self):
val = self.cleaned_data.get('identifier')
Question._clean_identifier(self.instance.event, val, self.instance)
@@ -163,7 +155,6 @@ class QuestionForm(I18nModelForm):
'type',
'required',
'ask_during_checkin',
'show_during_checkin',
'hidden',
'identifier',
'items',
@@ -388,7 +379,6 @@ class ItemCreateForm(I18nModelForm):
'max_per_order',
'generate_tickets',
'checkin_attention',
'checkin_text',
'free_price',
'original_price',
'sales_channels',
@@ -713,7 +703,6 @@ class ItemUpdateForm(I18nModelForm):
'max_per_order',
'min_per_order',
'checkin_attention',
'checkin_text',
'generate_tickets',
'original_price',
'require_bundling',
@@ -762,7 +751,6 @@ class ItemUpdateForm(I18nModelForm):
'show_quota_left': ShowQuotaNullBooleanSelect(),
'max_per_order': forms.widgets.NumberInput(attrs={'min': 0}),
'min_per_order': forms.widgets.NumberInput(attrs={'min': 0}),
'checkin_text': forms.TextInput(),
}
@@ -881,7 +869,6 @@ class ItemVariationForm(I18nModelForm):
'require_membership_hidden',
'require_membership_types',
'checkin_attention',
'checkin_text',
'available_from',
'available_until',
'sales_channels',
@@ -897,7 +884,6 @@ class ItemVariationForm(I18nModelForm):
'require_membership_types': forms.CheckboxSelectMultiple(attrs={
'class': 'scrolling-multiple-choice'
}),
'checkin_text': forms.TextInput(),
}
def clean(self):
+1 -2
View File
@@ -265,13 +265,12 @@ class ExporterForm(forms.Form):
class CommentForm(I18nModelForm):
class Meta:
model = Order
fields = ['comment', 'checkin_attention', 'checkin_text', 'custom_followup_at']
fields = ['comment', 'checkin_attention', 'custom_followup_at']
widgets = {
'comment': forms.Textarea(attrs={
'rows': 3,
'class': 'helper-width-100',
}),
'checkin_text': forms.TextInput(),
'custom_followup_at': DatePickerWidget(),
}
+5 -41
View File
@@ -39,7 +39,7 @@ from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.forms import formset_factory, inlineformset_factory
from django.forms import inlineformset_factory
from django.forms.utils import ErrorDict
from django.urls import reverse
from django.utils.crypto import get_random_string
@@ -48,7 +48,7 @@ from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django_scopes.forms import SafeModelChoiceField
from i18nfield.forms import (
I18nForm, I18nFormField, I18nFormSetMixin, I18nTextarea, I18nTextInput,
I18nFormField, I18nFormSetMixin, I18nTextarea, I18nTextInput,
)
from phonenumber_field.formfields import PhoneNumberField
from pytz import common_timezones
@@ -195,50 +195,14 @@ class SafeOrderPositionChoiceField(forms.ModelChoiceField):
return f'{op.order.code}-{op.positionid} ({str(op.item) + ((" - " + str(op.variation)) if op.variation else "")})'
class EventMetaPropertyForm(I18nModelForm):
class EventMetaPropertyForm(forms.ModelForm):
class Meta:
model = EventMetaProperty
fields = ['name', 'default', 'required', 'protected', 'filter_public', 'public_label', 'filter_allowed']
fields = ['name', 'default', 'required', 'protected', 'allowed_values', 'filter_allowed']
widgets = {
'default': forms.TextInput(),
'default': forms.TextInput()
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['public_label'].widget.attrs['data-display-dependency'] = '#id_filter_public'
class EventMetaPropertyAllowedValueForm(I18nForm):
key = forms.CharField(
label=_('Internal name'),
max_length=250,
required=True
)
label = I18nFormField(
label=_('Public name'),
required=False,
widget=I18nTextInput,
widget_kwargs=dict(attrs={
'placeholder': _('Public name'),
})
)
class I18nBaseFormSet(I18nFormSetMixin, forms.BaseFormSet):
# compatibility shim for django-i18nfield library
def __init__(self, *args, **kwargs):
self.organizer = kwargs.pop('organizer', None)
if self.organizer:
kwargs['locales'] = self.organizer.settings.get('locales')
super().__init__(*args, **kwargs)
EventMetaPropertyAllowedValueFormSet = formset_factory(
EventMetaPropertyAllowedValueForm, formset=I18nBaseFormSet,
can_order=True, can_delete=True, extra=0
)
class MembershipTypeForm(I18nModelForm):
class Meta:
+2 -2
View File
@@ -393,12 +393,12 @@ class SubEventMetaValueForm(forms.ModelForm):
self.default = kwargs.pop('default', None)
self.disabled = kwargs.pop('disabled', False)
super().__init__(*args, **kwargs)
if self.property.choices:
if self.property.allowed_values:
self.fields['value'] = forms.ChoiceField(
label=self.property.name,
choices=[
('', _('Default ({value})').format(value=self.default or self.property.default) if self.default or self.property.default else ''),
] + [(a.strip(), a.strip()) for a in self.property.choice_keys],
] + [(a.strip(), a.strip()) for a in self.property.allowed_values.splitlines()],
)
else:
self.fields['value'].label = self.property.name
-2
View File
@@ -201,8 +201,6 @@ class VoucherForm(I18nModelForm):
cnt = len(data['codes']) * data.get('max_usages', 0)
else:
cnt = data.get('max_usages', 0)
if self.instance and self.instance.pk:
cnt -= self.instance.redeemed # these do not need quota any more
Voucher.clean_item_properties(
data, self.instance.event,
-1
View File
@@ -407,7 +407,6 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
'pretix.event.order.custom_followup_at': _('The order\'s follow-up date has been updated.'),
'pretix.event.order.checkin_attention': _('The order\'s flag to require attention at check-in has been '
'toggled.'),
'pretix.event.order.checkin_text': _('The order\'s check-in text has been changed.'),
'pretix.event.order.pretix.event.order.valid_if_pending': _('The order\'s flag to be considered valid even if '
'unpaid has been toggled.'),
'pretix.event.order.payment.changed': _('A new payment {local_id} has been started instead of the previous one.'),
@@ -85,31 +85,11 @@
{% endif %}
{% endif %}
{% if result.position %}
{% if result.require_attention %}
{% if result.position.require_attention %}
<p>
<strong>
<span class="fa fa-info-circle text-info fa-fw"></span>
{% trans "Special attention required" %}
</strong>
<span class="fa fa-info-circle fa-fw"></span> {% trans "Special attention required" %}
</p>
{% endif %}
{% for t in result.checkin_texts %}
<p>
<span class="fa fa-info-circle text-muted fa-fw"></span>
{{ t }}
</p>
{% endfor %}
{% if result.position_object %}
{% for a in result.position_object.answers.all %}
{% if a.question.show_during_checkin %}
<p>
<span class="fa fa-question-circle text-muted fa-fw"></span>
<strong>{{ a.question.question }}</strong>
{{ a }}
</p>
{% endif %}
{% endfor %}
{% endif %}
<p>
<span class="fa fa-ticket fa-fw"></span>
<a href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=result.position.order %}">
@@ -14,7 +14,6 @@
{% bootstrap_field form.cancel_allow_user_unpaid_keep layout="control" %}
{% bootstrap_field form.cancel_allow_user_unpaid_keep_percentage layout="control" %}
{% bootstrap_field form.cancel_allow_user_unpaid_keep_fees layout="control" %}
{% bootstrap_field form.cancel_terms_unpaid layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Paid orders" %}</legend>
@@ -33,7 +32,6 @@
{% bootstrap_field form.cancel_allow_user_paid_adjust_fees_step layout="control" %}
</div>
{% bootstrap_field form.cancel_allow_user_paid_refund_as_giftcard layout="control" %}
{% bootstrap_field form.cancel_terms_paid layout="control" %}
{% if not gets_notification %}
<div class="alert alert-warning">
{% blocktrans trimmed %}
@@ -5,6 +5,7 @@
<summary class="panel-heading">
<h4 class="panel-title">
<strong>{% trans title %}</strong>
<i class="fa fa-angle-down collapse-indicator"></i>
</h4>
</summary>
<div id="{{ pid }}">
@@ -316,9 +316,6 @@
{% if sform.event_list_available_only %}
{% bootstrap_field sform.event_list_available_only layout="control" %}
{% endif %}
{% if sform.event_list_filters %}
{% bootstrap_field sform.event_list_filters layout="control" %}
{% endif %}
{% if sform.event_calendar_future_only %}
{% bootstrap_field sform.event_calendar_future_only layout="control" %}
{% endif %}
@@ -17,6 +17,7 @@
<div class="row">
<div class="col-md-4 col-xs-12">
<strong class="panel-title">
<span class="fa fa-fw chevron"></span>
<span class="fa fa-warning text-danger hidden variation-error"></span>
<span class="variation-name">
Variation name
@@ -108,7 +109,6 @@
</div>
{% endif %}
{% bootstrap_field form.checkin_attention layout="control" %}
{% bootstrap_field form.checkin_text layout="control" %}
</div>
</details>
{% endfor %}
@@ -125,6 +125,7 @@
<div class="row">
<div class="col-md-4 col-xs-12">
<strong class="panel-title">
<span class="fa fa-fw chevron"></span>
<span class="fa fa-warning text-danger hidden variation-error"></span>
<span class="variation-name">
{% trans "New variation" %}
@@ -207,7 +208,6 @@
</div>
{% endif %}
{% bootstrap_field formset.empty_form.checkin_attention layout="control" %}
{% bootstrap_field formset.empty_form.checkin_text layout="control" %}
</div>
</details>
{% endescapescript %}
@@ -202,7 +202,6 @@
<fieldset>
<legend>{% trans "Check-in & Validity" %}</legend>
{% bootstrap_field form.checkin_attention layout="control" %}
{% bootstrap_field form.checkin_text layout="control" %}
{% bootstrap_field form.validity_mode layout="control" %}
<div data-display-dependency="#{{ form.validity_mode.id_for_label }}" data-display-dependency-value="fixed">
{% bootstrap_field form.validity_fixed_from layout="control" %}
@@ -129,7 +129,6 @@
{% bootstrap_field form.help_text layout="control" %}
{% bootstrap_field form.identifier layout="control" %}
{% bootstrap_field form.ask_during_checkin layout="control" %}
{% bootstrap_field form.show_during_checkin layout="control" %}
{% bootstrap_field form.hidden layout="control" %}
{% bootstrap_field form.print_on_invoice layout="control" %}
@@ -25,6 +25,7 @@
<strong>{% trans "Invoice information" %} {% if not request.event.settings.invoice_address_required %}
{% trans "(optional)" %}
{% endif %}</strong>
<i class="fa fa-angle-down collapse-indicator"></i>
</h4>
</summary>
<div id="invoice">
@@ -41,6 +42,7 @@
<strong>{{ pos.item }}{% if pos.variation %}
{{ pos.variation }}
{% endif %}</strong>
<i class="fa fa-angle-down collapse-indicator"></i>
</h4>
</summary>
<div id="cp{{ pos.id }}">
@@ -589,9 +589,9 @@
</a>
{% endif %}
{% elif q.type == "M" %}
{{ q.answer.to_string_i18n|rich_text_snippet }}
{{ q.answer|rich_text_snippet }}
{% else %}
{{ q.answer.to_string_i18n|linebreaksbr }}
{{ q.answer|linebreaksbr }}
{% endif %}
{% else %}
<em>{% trans "not answered" %}</em>
@@ -997,7 +997,6 @@
{% bootstrap_field comment_form.comment show_help=True show_label=False %}
{% bootstrap_field comment_form.custom_followup_at %}
{% bootstrap_field comment_form.checkin_attention show_help=True show_label=False %}
{% bootstrap_field comment_form.checkin_text show_help=True show_label=False %}
{% if "can_change_orders" in request.eventpermset %}
<button class="btn btn-default">
{% trans "Update comment" %}
@@ -25,17 +25,6 @@
<fieldset>
<legend>{% trans "Export options" %}</legend>
{% bootstrap_form exporter.form layout='control' %}
{% if "_format" in exporter.form.fields and exporter.multisheet_warning %}
<div data-display-dependency="#id_{{ exporter.form.prefix }}-_format" data-display-dependency-value="xlsx">
<div class="alert alert-info">
{% blocktrans trimmed %}
Your generated Excel file will have <strong>multiple sheets</strong>. Some data you are
looking for might not be on the first sheet.
{% endblocktrans %}
</div>
</div>
{% endif %}
</fieldset>
{% if schedule_form %}
{% include "pretixcontrol/orders/fragment_export_schedule_form.html" %}
@@ -1,6 +1,5 @@
{% load i18n %}
{% load bootstrap3 %}
{# Changes should be replicated in pretixpresale/event/fragment_order_status.html and in pretix/base/models/orders.py #}
{% if order.status == "n" %}
{% if order.require_approval %}
<span class="label label-warning {{ class }}">
@@ -299,13 +299,6 @@
{% trans "Deny" %}
</button>
</li>
<li>
<button type="submit" class="btn"
formaction="{% url "control:event.orders.bulk.refund_overpaid" organizer=request.organizer.slug event=request.event.slug %}">
<i class="fa fa-money fa-fw text-danger"></i>
{% trans "Refund overpaid amount" %}
</button>
</li>
{% if not request.event.settings.payment_term_expire_automatically %}
<li>
<button type="submit" class="btn"
@@ -26,17 +26,6 @@
<fieldset>
<legend>{% trans "Export options" %}</legend>
{% bootstrap_form exporter.form layout='control' %}
{% if "_format" in exporter.form.fields and exporter.multisheet_warning %}
<div data-display-dependency="#id_{{ exporter.form.prefix }}-_format" data-display-dependency-value="xlsx">
<div class="alert alert-info">
{% blocktrans trimmed %}
Your generated Excel file will have <strong>multiple sheets</strong>. Some data you are
looking for might not be on the first sheet.
{% endblocktrans %}
</div>
</div>
{% endif %}
</fieldset>
{% if schedule_form %}
{% include "pretixcontrol/orders/fragment_export_schedule_form.html" %}
@@ -14,56 +14,29 @@
<span class="fa fa-plus"></span>
{% trans "Create a new property" %}
</a>
<form method="post">
{% csrf_token %}
<table class="table table-condensed table-hover">
<thead>
<table class="table table-condensed table-hover">
<thead>
<tr>
<th>{% trans "Property" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for p in properties %}
<tr>
<th>{% trans "Property" %}</th>
<th class="iconcol"></th>
<th class="iconcol"></th>
<th class="iconcol"></th>
<th class="action-col-2"></th>
<th class="action-col-2"></th>
<td><strong>
<a href="{% url "control:organizer.property.edit" organizer=request.organizer.slug property=p.id %}">
{{ p.name }}
</a>
</strong></td>
<td class="text-right flip">
<a href="{% url "control:organizer.property.edit" organizer=request.organizer.slug property=p.id %}"
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
<a href="{% url "control:organizer.property.delete" organizer=request.organizer.slug property=p.id %}"
class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
</td>
</tr>
</thead>
<tbody data-dnd-url="{% url "control:organizer.properties.reorder" organizer=request.organizer.slug %}">
{% for p in properties %}
<tr data-dnd-id="{{ p.pk }}">
<td><strong>
<a href="{% url "control:organizer.property.edit" organizer=request.organizer.slug property=p.id %}">
{{ p.name }}
</a>
</strong></td>
<td>
{% if p.filter_allowed %}
<span class="fa fa-filter text-muted" data-toggle="tooltip" title="{% trans "Can be used for filtering" %}"></span>
{% endif %}
</td>
<td>
{% if p.filter_public %}
<span class="fa fa-eye text-muted" data-toggle="tooltip" title="{% trans "Show filter option to customers" %}"></span>
{% endif %}
</td>
<td>
{% if p.protected %}
<span class="fa fa-lock text-muted" data-toggle="tooltip" title="{% trans "Can only be changed by organizer-level administrators" %}"></span>
{% endif %}
</td>
<td>
<button formaction="{% url "control:organizer.property.up" organizer=request.organizer.slug property=p.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 and not page_obj.has_previous %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
<button formaction="{% url "control:organizer.property.down" organizer=request.organizer.slug property=p.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
<span class="dnd-container"></span>
</td>
<td class="text-right flip">
<a href="{% url "control:organizer.property.edit" organizer=request.organizer.slug property=p.id %}"
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
<a href="{% url "control:organizer.property.delete" organizer=request.organizer.slug property=p.id %}"
class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
{% endfor %}
</tbody>
</table>
{% endblock %}
@@ -1,6 +1,5 @@
{% extends "pretixcontrol/organizers/base.html" %}
{% load i18n %}
{% load formset_tags %}
{% load bootstrap3 %}
{% block inner %}
{% if property %}
@@ -10,94 +9,7 @@
{% endif %}
<form class="form-horizontal" action="" method="post">
{% csrf_token %}
{% bootstrap_form_errors form layout="control" %}
<fieldset>
<legend>{% trans "General" %}</legend>
{% bootstrap_field form.name layout="control" %}
{% bootstrap_field form.default layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Usage" %}</legend>
{% bootstrap_field form.filter_allowed layout="control" %}
{% bootstrap_field form.filter_public layout="control" %}
{% bootstrap_field form.public_label layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Validation" %}</legend>
{% bootstrap_field form.required layout="control" %}
{% bootstrap_field form.protected layout="control" %}
<div class="form-group">
<label class="col-md-3 control-label">
{% trans "Allowed values" %}<br>
<span class="optional">{% trans "Optional" %}</span>
</label>
<div class="col-md-9">
<p class="help-block">{% trans "If you keep this empty, all input will be allowed." %}</p>
<div class="formset tax-rules-formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="row tax-rule-line" data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="col-sm-6 col-md-4 col-lg-5">
{% bootstrap_field formset.empty_form.key layout='inline' form_group_class="" %}
</div>
<div class="col-sm-6 col-md-4 col-lg-5">
{% bootstrap_field formset.empty_form.label layout='inline' form_group_class="" %}
</div>
<div class="col-sm-6 col-md-3 col-lg-2 text-right flip">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endescapescript %}
</script>
<div data-formset-body class="tax-rule-lines">
{% for form in formset %}
{% bootstrap_form_errors form %}
<div class="row tax-rule-line" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="col-sm-6 col-md-4 col-lg-5">
{% bootstrap_field form.key layout='inline' form_group_class="" %}
</div>
<div class="col-sm-6 col-md-4 col-lg-5">
{% bootstrap_field form.label layout='inline' form_group_class="" %}
</div>
<div class="col-sm-6 col-md-3 col-lg-2 text-right flip">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endfor %}
</div>
<div class="row tax-rule-line" data-formset-form>
<div class="col-sm-12">
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new value" %}</button>
</div>
</div>
</div>
</div>
</div>
</fieldset>
{% bootstrap_form form layout="control" %}
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
@@ -6,48 +6,26 @@
<ul class="pagination">
{% if is_paginated %}
{% if page_obj.has_previous %}
{% if page_obj.previous_page_number > 1 %}
<li>
<a href="?{% url_replace request 'page' page_obj.num_pages %}" title="{% trans "Go to page 1" %}">
<span class="fa fa-angle-double-left"></span>
</a>
</li>
{% endif %}
<li>
<a href="?{% url_replace request 'page' page_obj.previous_page_number %}" title="{% blocktrans with page=page_obj.previous_page_number %}Go to page {{ page }}{% endblocktrans %}">
<span class="fa fa-angle-left"></span>
<a href="?{% url_replace request 'page' page_obj.previous_page_number %}">
<span>&laquo;</span>
</a>
</li>
{% endif %}
<li class="page-current">
<a {% if page_obj.paginator.count %}
class="pagination-selection"
data-href="?{% url_replace request 'page' '_PAGE_' %}"
data-max="{{ page_obj.paginator.num_pages }}"
title="{% trans "Click to choose a page" %}"
href="#"
{% endif %}>
<li class="page-current"><a>
{% blocktrans trimmed with page=page_obj.number of=page_obj.paginator.num_pages count=page_obj.paginator.count|intcomma %}
Page {{ page }} of {{ of }} ({{ count }} elements)
{% endblocktrans %}
</a>
</li>
</a></li>
{% if page_obj.has_next %}
<li>
<a href="?{% url_replace request 'page' page_obj.next_page_number %}" title="{% blocktrans with page=page_obj.next_page_number %}Go to page {{ page }}{% endblocktrans %}">
<span class="fa fa-angle-right"></span>
</a>
</li>
{% endif %}
{% if page_obj.paginator.count and page_obj.paginator.num_pages > page_obj.next_page_number %}
<li>
<a href="?{% url_replace request 'page' page_obj.paginator.num_pages %}" title="{% blocktrans with page=page_obj.paginator.num_pages %}Go to page {{ page }}{% endblocktrans %}">
<span class="fa fa-angle-double-right"></span>
<a href="?{% url_replace request 'page' page_obj.next_page_number %}">
<span>&raquo;</span>
</a>
</li>
{% endif %}
{% else %}
{% if page_obj.paginator.count > 1 %}
{% if page_obj.paginator.count > 1 %}
<li class="page-current"><a>
{% blocktrans trimmed with count=page_obj.paginator.count|intcomma %}
{{ count }} elements
@@ -11,7 +11,7 @@
{% else %}
<h1>{% trans "Date" context "subevent" %}</h1>
{% endif %}
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
<form action="" method="post" class="form-horizontal" enctype="multipart/form-datai">
{% csrf_token %}
{% bootstrap_form_errors form %}
{% for f in itemvar_forms %}
@@ -133,11 +133,6 @@
{{ s.name }}</a></strong><br>
<small class="text-muted">
#{{ s.pk }}
{% for k, v in s.meta_data.items %}
{% if v %}
<small class="text-muted">&middot; {{ k }}: {{ v }}</small>
{% endif %}
{% endfor %}
</small>
</td>
<td>
-7
View File
@@ -126,12 +126,6 @@ urlpatterns = [
name='organizer.property.edit'),
re_path(r'^organizer/(?P<organizer>[^/]+)/property/(?P<property>[^/]+)/delete$', organizer.EventMetaPropertyDeleteView.as_view(),
name='organizer.property.delete'),
re_path(r'^organizer/(?P<organizer>[^/]+)/property/(?P<property>[^/]+)/up$', organizer.meta_property_move_up,
name='organizer.property.up'),
re_path(r'^organizer/(?P<organizer>[^/]+)/property/(?P<property>[^/]+)/down$', organizer.meta_property_move_down,
name='organizer.property.down'),
re_path(r'^organizer/(?P<organizer>[^/]+)/property/reorder$', organizer.reorder_meta_properties,
name='organizer.properties.reorder'),
re_path(r'^organizer/(?P<organizer>[^/]+)/membershiptypes$', organizer.MembershipTypeListView.as_view(), name='organizer.membershiptypes'),
re_path(r'^organizer/(?P<organizer>[^/]+)/membershiptype/add$', organizer.MembershipTypeCreateView.as_view(),
name='organizer.membershiptype.add'),
@@ -423,7 +417,6 @@ urlpatterns = [
re_path(r'^orders/bulk/approve$', orders.OrderApproveBulkActionView.as_view(), name='event.orders.bulk.approve'),
re_path(r'^orders/bulk/deny$', orders.OrderDenyBulkActionView.as_view(), name='event.orders.bulk.deny'),
re_path(r'^orders/bulk/expire$', orders.OrderExpireBulkActionView.as_view(), name='event.orders.bulk.expire'),
re_path(r'^orders/bulk/refund_overpaid$', orders.OrderOverpaidRefundBulkActionView.as_view(), name='event.orders.bulk.refund_overpaid'),
re_path(r'^orders/bulk/delete$', orders.OrderDeleteBulkActionView.as_view(), name='event.orders.bulk.delete'),
re_path(r'^orders/search$', orders.OrderSearch.as_view(), name='event.orders.search'),
re_path(r'^dangerzone/$', event.DangerZone.as_view(), name='event.dangerzone'),
+3 -4
View File
@@ -266,7 +266,7 @@ class Forgot(TemplateView):
has_redis = settings.HAS_REDIS
try:
user = User.objects.get(is_active=True, auth_backend='native', email__iexact=email)
user = User.objects.get(email__iexact=email)
if has_redis:
from django_redis import get_redis_connection
@@ -322,8 +322,7 @@ class Recover(TemplateView):
}
def dispatch(self, request, *args, **kwargs):
# settings.PRETIX_PASSWORD_RESET is not checked here to allow admin-sent recovery links
if 'native' not in get_auth_backends():
if not settings.PRETIX_PASSWORD_RESET or 'native' not in get_auth_backends():
raise PermissionDenied('Registration is disabled')
return super().dispatch(request, *args, **kwargs)
@@ -331,7 +330,7 @@ class Recover(TemplateView):
if request.user.is_authenticated:
return redirect(request.GET.get("next", 'control:index'))
try:
user = User.objects.get(id=self.request.GET.get('id'), is_active=True, auth_backend='native')
user = User.objects.get(id=self.request.GET.get('id'), auth_backend='native')
except User.DoesNotExist:
return self.invalid('unknownuser')
if not default_token_generator.check_token(user, self.request.GET.get('token')):
+1 -4
View File
@@ -551,12 +551,9 @@ class CheckInListSimulator(EventPermissionRequiredMixin, FormView):
gate=form.cleaned_data.get("gate"),
).data
if self.result.get("position"):
op = OrderPosition.objects.get(pk=self.result["position"]["id"])
self.result["position_object"] = op
if form.cleaned_data["checkin_type"] == Checkin.TYPE_ENTRY and self.list.rules and self.result.get("position")\
and (self.result["status"] in ("ok", "incomplete") or self.result["reason"] == "rules"):
op = OrderPosition.objects.get(pk=self.result["position"]["id"])
rule_data = LazyRuleVars(op, self.list, form.cleaned_data["datetime"], form.cleaned_data.get("gate"))
rule_graph = _logic_annotate_for_graphic_explain(self.list.rules, op.subevent or self.list.event, rule_data,
form.cleaned_data["datetime"])
+2 -11
View File
@@ -32,7 +32,6 @@
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
import csv
import logging
from datetime import timedelta
@@ -158,14 +157,6 @@ class ProcessView(EventPermissionRequiredMixin, AsyncAction, FormView):
)
return parse_csv(self.file.file, 1024 * 1024, "replace", charset=charset)
@cached_property
def parsed_list(self):
try:
return list(self.parsed)
except csv.Error:
logger.exception("Could not parse full CSV file")
return None
def get(self, request, *args, **kwargs):
if 'async_id' in request.GET and settings.HAS_CELERY:
return self.get_result(request)
@@ -183,7 +174,7 @@ class ProcessView(EventPermissionRequiredMixin, AsyncAction, FormView):
def dispatch(self, request, *args, **kwargs):
if 'async_id' in request.GET and settings.HAS_CELERY:
return self.get_result(request)
if not self.parsed or not self.parsed_list:
if not self.parsed:
messages.error(request, _('We\'ve been unable to parse the uploaded file as a CSV file.'))
return redirect(reverse('control:event.orders.import', kwargs={
'event': request.event.slug,
@@ -202,5 +193,5 @@ class ProcessView(EventPermissionRequiredMixin, AsyncAction, FormView):
ctx = super().get_context_data(**kwargs)
ctx['file'] = self.file
ctx['parsed'] = self.parsed
ctx['sample_rows'] = self.parsed_list[:3]
ctx['sample_rows'] = list(self.parsed)[:3]
return ctx
+5 -53
View File
@@ -74,7 +74,6 @@ from i18nfield.strings import LazyI18nString
from pretix.base.channels import get_all_sales_channels
from pretix.base.decimal import round_decimal
from pretix.base.email import get_email_context
from pretix.base.exporter import MultiSheetListExporter
from pretix.base.i18n import language
from pretix.base.models import (
CachedCombinedTicket, CachedFile, CachedTicket, Checkin, Invoice,
@@ -213,14 +212,10 @@ class BaseOrderBulkActionView(OrderSearchMixin, EventPermissionRequiredMixin, As
def execute_bulk(self, queryset: QuerySet, form: forms.Form):
qs = self.allowed_for(self.allowed_for(self.get_queryset()))
total = qs.count()
orders_with_successful_action = 0
for i, o in enumerate(qs):
res = self.execute_single(o, form)
if res:
orders_with_successful_action += 1
self.execute_single(o, form)
if i % 100 == 0:
self.async_set_progress(i / total * 100)
return orders_with_successful_action, total
def get_error_url(self):
return self.get_success_url(None)
@@ -236,9 +231,6 @@ class BaseOrderBulkActionView(OrderSearchMixin, EventPermissionRequiredMixin, As
'organizer': self.request.event.organizer.slug,
})
def get_success_message(self, value):
return _("Successfully executed the action \"{label}\" on {success} of {total} orders.").format(success=value[0], label=self.label, total=value[1])
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['total'] = self.get_queryset().count()
@@ -278,7 +270,7 @@ class BaseOrderBulkActionView(OrderSearchMixin, EventPermissionRequiredMixin, As
@transaction.atomic()
def async_form_valid(self, task, form):
return self.execute_bulk(self.allowed_for(self.get_queryset()), form)
self.execute_bulk(self.allowed_for(self.get_queryset()), form)
class OrderApproveBulkActionView(BaseOrderBulkActionView):
@@ -292,7 +284,6 @@ class OrderApproveBulkActionView(BaseOrderBulkActionView):
def execute_single(self, instance, form: forms.Form):
approve_order(instance, user=self.request.user)
return True
class OrderDenyBulkActionView(BaseOrderBulkActionView):
@@ -309,7 +300,6 @@ class OrderDenyBulkActionView(BaseOrderBulkActionView):
deny_order(instance, user=self.request.user,
comment=form.cleaned_data.get('comment') or None,
send_mail=form.cleaned_data['send_email'])
return True
class OrderExpireBulkActionView(BaseOrderBulkActionView):
@@ -324,33 +314,6 @@ class OrderExpireBulkActionView(BaseOrderBulkActionView):
def execute_single(self, instance, form: forms.Form):
mark_order_expired(instance, user=self.request.user)
return True
class OrderOverpaidRefundBulkActionView(BaseOrderBulkActionView):
label = _("Refund overpaid amount")
def allowed_for(self, queryset):
return Order.annotate_overpayments(queryset).filter(is_overpaid=True)
def execute_single(self, instance: Order, form: forms.Form):
if instance.pending_sum < 0:
try:
proposals = instance.propose_auto_refunds(instance.pending_sum * -1)
for payment, amount in proposals.items():
refund = OrderRefund.objects.create(
order=instance,
payment=payment,
source=OrderRefund.REFUND_SOURCE_ADMIN,
state=OrderRefund.REFUND_STATE_CREATED,
amount=amount,
comment=_("Refund for overpayment"),
provider=payment.provider
)
payment.payment_provider.execute_refund(refund)
return True
except (ValueError, PaymentException):
return False
class OrderDeleteBulkActionView(BaseOrderBulkActionView):
@@ -513,8 +476,7 @@ class OrderDetail(OrderView):
ctx['comment_form'] = CommentForm(initial={
'comment': self.order.comment,
'custom_followup_at': self.order.custom_followup_at,
'checkin_attention': self.order.checkin_attention,
'checkin_text': self.order.checkin_text,
'checkin_attention': self.order.checkin_attention
})
ctx['display_locale'] = dict(settings.LANGUAGES)[self.object.locale or self.request.event.settings.locale]
@@ -748,13 +710,7 @@ class OrderComment(OrderView):
self.order.log_action('pretix.event.order.checkin_attention', user=self.request.user, data={
'new_value': form.cleaned_data.get('checkin_attention')
})
if form.cleaned_data.get('checkin_text') != self.order.checkin_text:
self.order.checkin_text = form.cleaned_data.get('checkin_text')
self.order.log_action('pretix.event.order.checkin_text', user=self.request.user, data={
'new_value': form.cleaned_data.get('checkin_text')
})
self.order.save(update_fields=['checkin_attention', 'checkin_text', 'comment', 'custom_followup_at'])
self.order.save(update_fields=['checkin_attention', 'comment', 'custom_followup_at'])
self.order.refresh_from_db()
messages.success(self.request, _('The comment has been updated.'))
else:
@@ -1185,9 +1141,6 @@ class OrderRefundView(OrderView):
messages.error(self.request, _('You entered an order that could not be found.'))
is_valid = False
else:
if order.event.currency != self.request.event.currency:
messages.error(self.request, _('You entered an order in an event with a different currency.'))
is_valid = False
refunds.append(OrderRefund(
order=self.order,
payment=None,
@@ -1938,7 +1891,7 @@ class OrderChange(OrderView):
ocm.cancel_fee(f)
continue
if f.form.cleaned_data['value'] is not None and f.form.cleaned_data['value'] != f.value:
if f.form.cleaned_data['value'] != f.value:
ocm.change_fee(f, f.form.cleaned_data['value'])
if f.form.cleaned_data['tax_rule'] and f.form.cleaned_data['tax_rule'] != f.tax_rule:
@@ -2523,7 +2476,6 @@ class ExportMixin:
prefix=ex.identifier,
initial=initial
)
ex.multisheet_warning = isinstance(ex, MultiSheetListExporter) and len(ex.sheets) > 1
ex.form.fields = ex.export_form_fields
return ex
+16 -138
View File
@@ -37,7 +37,6 @@ import re
from datetime import time, timedelta
from decimal import Decimal
from hashlib import sha1
from json import JSONDecodeError
import bleach
import dateutil
@@ -53,9 +52,7 @@ from django.db.models import (
)
from django.db.models.functions import Coalesce, Greatest
from django.forms import DecimalField
from django.http import (
Http404, HttpResponse, HttpResponseBadRequest, JsonResponse,
)
from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.formats import date_format
@@ -63,7 +60,6 @@ from django.utils.functional import cached_property
from django.utils.timezone import get_current_timezone, now
from django.utils.translation import gettext, gettext_lazy as _
from django.views import View
from django.views.decorators.http import require_http_methods
from django.views.generic import (
CreateView, DetailView, FormView, ListView, TemplateView, UpdateView,
)
@@ -72,9 +68,7 @@ from pretix.api.models import ApiCall, WebHook
from pretix.api.webhooks import manually_retry_all_calls
from pretix.base.auth import get_auth_backends
from pretix.base.channels import get_all_sales_channels
from pretix.base.exporter import (
MultiSheetListExporter, OrganizerLevelExportMixin,
)
from pretix.base.exporter import OrganizerLevelExportMixin
from pretix.base.i18n import language
from pretix.base.models import (
CachedFile, Customer, Device, Gate, GiftCard, Invoice, LogEntry,
@@ -103,19 +97,17 @@ from pretix.control.forms.filter import (
from pretix.control.forms.orders import ExporterForm
from pretix.control.forms.organizer import (
CustomerCreateForm, CustomerUpdateForm, DeviceBulkEditForm, DeviceForm,
EventMetaPropertyAllowedValueFormSet, EventMetaPropertyForm, GateForm,
GiftCardAcceptanceInviteForm, GiftCardCreateForm, GiftCardUpdateForm,
MailSettingsForm, MembershipTypeForm, MembershipUpdateForm,
OrganizerDeleteForm, OrganizerFooterLinkFormset, OrganizerForm,
OrganizerSettingsForm, OrganizerUpdateForm, ReusableMediumCreateForm,
ReusableMediumUpdateForm, SSOClientForm, SSOProviderForm, TeamForm,
WebHookForm,
EventMetaPropertyForm, GateForm, GiftCardAcceptanceInviteForm,
GiftCardCreateForm, GiftCardUpdateForm, MailSettingsForm,
MembershipTypeForm, MembershipUpdateForm, OrganizerDeleteForm,
OrganizerFooterLinkFormset, OrganizerForm, OrganizerSettingsForm,
OrganizerUpdateForm, ReusableMediumCreateForm, ReusableMediumUpdateForm,
SSOClientForm, SSOProviderForm, TeamForm, WebHookForm,
)
from pretix.control.forms.rrule import RRuleForm
from pretix.control.logdisplay import OVERVIEW_BANLIST
from pretix.control.permissions import (
AdministratorPermissionRequiredMixin, OrganizerPermissionRequiredMixin,
organizer_permission_required,
)
from pretix.control.signals import nav_organizer
from pretix.control.views import PaginationMixin
@@ -1667,7 +1659,6 @@ class ExportMixin:
prefix=ex.identifier,
initial=initial
)
ex.multisheet_warning = isinstance(ex, MultiSheetListExporter) and len(ex.sheets) > 1
ex.form.fields = ex.export_form_fields
if not isinstance(ex, OrganizerLevelExportMixin):
ex.form.fields.update([
@@ -2052,54 +2043,14 @@ class EventMetaPropertyListView(OrganizerDetailViewMixin, OrganizerPermissionReq
return self.request.organizer.meta_properties.all()
class EventMetaPropertyEditorMixin:
class EventMetaPropertyCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
model = EventMetaProperty
template_name = 'pretixcontrol/organizers/property_edit.html'
permission = 'can_change_organizer_settings'
form_class = EventMetaPropertyForm
@cached_property
def formset(self):
return EventMetaPropertyAllowedValueFormSet(
data=self.request.POST if self.request.method == "POST" else None,
organizer=self.request.organizer,
initial=(self.object.choices or []) if self.object else [],
)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['formset'] = self.formset
return ctx
def get_form_kwargs(self):
return {
**super().get_form_kwargs(),
'event': self.request.organizer,
}
def is_default_valid(self):
choice_keys = [
f.cleaned_data.get("key") for f in self.formset.ordered_forms if f not in self.formset.deleted_forms
]
default = self.form.cleaned_data["default"]
if default and choice_keys and default not in choice_keys:
messages.error(self.request, _("You cannot set a default value that is not a valid value."))
return False
return True
def post(self, request, *args, **kwargs):
self.object = self.get_object(self.get_queryset())
self.form = self.get_form()
if self.form.is_valid() and self.formset.is_valid() and self.is_default_valid():
return self.form_valid(self.form)
else:
return self.form_invalid(self.form)
class EventMetaPropertyCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, EventMetaPropertyEditorMixin, CreateView):
model = EventMetaProperty
permission = 'can_change_organizer_settings'
def get_object(self, queryset=None):
return EventMetaProperty()
return get_object_or_404(EventMetaProperty, organizer=self.request.organizer, pk=self.kwargs.get('property'))
def get_success_url(self):
return reverse('control:organizer.properties', kwargs={
@@ -2109,9 +2060,6 @@ class EventMetaPropertyCreateView(OrganizerDetailViewMixin, OrganizerPermissionR
def form_valid(self, form):
messages.success(self.request, _('The property has been created.'))
form.instance.organizer = self.request.organizer
form.instance.choices = [
f.cleaned_data for f in self.formset.ordered_forms if f not in self.formset.deleted_forms
]
ret = super().form_valid(form)
form.instance.log_action('pretix.property.created', user=self.request.user, data={
k: getattr(self.object, k) for k in form.changed_data
@@ -2123,10 +2071,12 @@ class EventMetaPropertyCreateView(OrganizerDetailViewMixin, OrganizerPermissionR
return super().form_invalid(form)
class EventMetaPropertyUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, EventMetaPropertyEditorMixin, UpdateView):
class EventMetaPropertyUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
model = EventMetaProperty
template_name = 'pretixcontrol/organizers/property_edit.html'
permission = 'can_change_organizer_settings'
context_object_name = 'property'
form_class = EventMetaPropertyForm
def get_object(self, queryset=None):
return get_object_or_404(EventMetaProperty, organizer=self.request.organizer, pk=self.kwargs.get('property'))
@@ -2137,10 +2087,7 @@ class EventMetaPropertyUpdateView(OrganizerDetailViewMixin, OrganizerPermissionR
})
def form_valid(self, form):
form.instance.choices = [
f.cleaned_data for f in self.formset.ordered_forms if f not in self.formset.deleted_forms
]
if form.has_changed() or self.formset.has_changed():
if form.has_changed():
self.object.log_action('pretix.property.changed', user=self.request.user, data={
k: getattr(self.object, k)
for k in form.changed_data
@@ -2177,75 +2124,6 @@ class EventMetaPropertyDeleteView(OrganizerDetailViewMixin, OrganizerPermissionR
return redirect(success_url)
def meta_property_move(request, property, up=True):
property = get_object_or_404(request.organizer.meta_properties, id=property)
properties = list(request.organizer.meta_properties.order_by("position"))
index = properties.index(property)
if index != 0 and up:
properties[index - 1], properties[index] = properties[index], properties[index - 1]
elif index != len(properties) - 1 and not up:
properties[index + 1], properties[index] = properties[index], properties[index + 1]
for i, prop in enumerate(properties):
if prop.position != i:
prop.position = i
prop.save()
prop.log_action(
'pretix.property.reordered', user=request.user, data={
'position': i,
}
)
messages.success(request, _('The order of properties has been updated.'))
@organizer_permission_required("can_change_organizer_settings")
@require_http_methods(["POST"])
def meta_property_move_up(request, organizer, property):
meta_property_move(request, property, up=True)
return redirect('control:organizer.properties',
organizer=request.organizer.slug)
@organizer_permission_required("can_change_organizer_settings")
@require_http_methods(["POST"])
def meta_property_move_down(request, organizer, property):
meta_property_move(request, property, up=False)
return redirect('control:organizer.properties',
organizer=request.organizer.slug)
@transaction.atomic
@organizer_permission_required("can_change_items")
@require_http_methods(["POST"])
def reorder_meta_properties(request, organizer):
try:
ids = json.loads(request.body.decode('utf-8'))['ids']
except (JSONDecodeError, KeyError, ValueError):
return HttpResponseBadRequest("expected JSON: {ids:[]}")
input_meta_properties = list(request.organizer.meta_properties.filter(id__in=[i for i in ids if i.isdigit()]))
if len(input_meta_properties) != len(ids):
raise Http404(_("Some of the provided object ids are invalid."))
if len(input_meta_properties) != request.organizer.meta_properties.count():
raise Http404(_("Not all objects have been selected."))
for c in input_meta_properties:
pos = ids.index(str(c.pk))
if pos != c.position: # Save unneccessary UPDATE queries
c.position = pos
c.save(update_fields=['position'])
c.log_action(
'pretix.property.reordered', user=request.user, data={
'position': pos,
}
)
return HttpResponse()
class LogView(OrganizerPermissionRequiredMixin, PaginationMixin, ListView):
template_name = 'pretixcontrol/organizers/logs.html'
permission = 'can_change_organizer_settings'
+5 -8
View File
@@ -120,10 +120,7 @@ class SubEventList(EventPermissionRequiredMixin, PaginationMixin, SubEventQueryM
permission = 'can_change_settings'
def get_queryset(self):
return super().get_queryset(True).prefetch_related(
'meta_values',
'meta_values__property',
)
return super().get_queryset(True)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
@@ -758,16 +755,16 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Asyn
initial['time_to'] = i.date_to.astimezone(tz).time() if i.date_to else None
initial['time_admission'] = i.date_admission.astimezone(tz).time() if i.date_admission else None
initial['rel_presale_start'] = RelativeDateWrapper(RelativeDate(
days=(i.date_from.astimezone(tz).date() - i.presale_start.astimezone(tz).date()).days,
days_before=(i.date_from.astimezone(tz).date() - i.presale_start.astimezone(tz).date()).days,
base_date_name='date_from',
time=i.presale_start.astimezone(tz).time(),
minutes=None
minutes_before=None
)) if i.presale_start else None
initial['rel_presale_end'] = RelativeDateWrapper(RelativeDate(
days=(i.date_from.astimezone(tz).date() - i.presale_end.astimezone(tz).date()).days,
days_before=(i.date_from.astimezone(tz).date() - i.presale_end.astimezone(tz).date()).days,
base_date_name='date_from',
time=i.presale_end.astimezone(tz).time(),
minutes=None
minutes_before=None
)) if i.presale_end else None
else:
kwargs['instance'] = SubEvent(event=self.request.event)
+1 -1
View File
@@ -267,7 +267,7 @@ class WaitingListView(EventPermissionRequiredMixin, WaitingListQuerySetMixin, Pa
free_seats = num_free_seats_for_product - num_valid_vouchers_for_product
wle.availability = (
Quota.AVAILABILITY_GONE if free_seats == 0 else wle.availability[0],
min(free_seats, wle.availability[1]) if wle.availability[1] is not None else free_seats,
min(free_seats, wle.availability[1])
)
itemvar_cache[(wle.item, wle.variation, wle.subevent)] = wle.availability
File diff suppressed because it is too large Load Diff
+24 -39
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-28 14:51+0000\n"
"POT-Creation-Date: 2023-10-18 07:26+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"
@@ -129,19 +129,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:164
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:225
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:213
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:246
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:264
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:254
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:247
msgid "Payment method unavailable"
msgstr ""
@@ -159,15 +158,15 @@ msgstr ""
msgid "Total revenue"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:14
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
msgid "Contacting Stripe …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:71
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
msgid "Total"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:220
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
msgid "Contacting your bank …"
msgstr ""
@@ -302,33 +301,17 @@ msgid "Ticket code is ambiguous on list"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order not approved"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Checked-in Tickets"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Valid Tickets"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Currently inside"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:69
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:70
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr ""
#: pretix/static/lightbox/js/lightbox.js:96
msgid "close"
msgstr ""
@@ -599,23 +582,15 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:778
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:781
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:939
#: pretix/static/pretixcontrol/js/ui/main.js:926
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:979
#: pretix/static/pretixcontrol/js/ui/main.js:966
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1054
#: pretix/static/pretixcontrol/js/ui/main.js:1041
msgid "You have unsaved changes!"
msgstr ""
@@ -631,6 +606,16 @@ msgstr ""
msgid "Count"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/subevent.js:111
msgid "(one more date)"
msgid_plural "({num} more dates)"
File diff suppressed because it is too large Load Diff
+24 -39
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-28 14:51+0000\n"
"POT-Creation-Date: 2023-10-18 07:26+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/"
@@ -133,19 +133,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:164
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "المتابعة"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:225
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:213
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:246
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:264
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
msgid "Confirming your payment …"
msgstr "جاري تأكيد الدفع الخاص بك …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:254
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:247
msgid "Payment method unavailable"
msgstr ""
@@ -163,15 +162,15 @@ msgstr "الطلبات المدفوعة"
msgid "Total revenue"
msgstr "إجمالي الإيرادات"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:14
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
msgid "Contacting Stripe …"
msgstr "جاري الاتصال بStripe …"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:71
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
msgid "Total"
msgstr "المجموع"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:220
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
msgid "Contacting your bank …"
msgstr "جاري الاتصال بالبنك الذي تتعامل معه …"
@@ -314,33 +313,17 @@ msgid "Ticket code is ambiguous on list"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order not approved"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Checked-in Tickets"
msgstr "تذاكر الدخول"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Valid Tickets"
msgstr "تذاكر سارية المفعول"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Currently inside"
msgstr "حاليا بالداخل"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:69
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr "نعم"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:70
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr "لا"
#: pretix/static/lightbox/js/lightbox.js:96
#, fuzzy
#| msgctxt "widget"
@@ -625,23 +608,15 @@ msgstr "لا شيء"
msgid "Selected only"
msgstr "المختارة فقط"
#: pretix/static/pretixcontrol/js/ui/main.js:778
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:781
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:939
#: pretix/static/pretixcontrol/js/ui/main.js:926
msgid "Use a different name internally"
msgstr "قم باستخدم اسم مختلف داخليا"
#: pretix/static/pretixcontrol/js/ui/main.js:979
#: pretix/static/pretixcontrol/js/ui/main.js:966
msgid "Click to close"
msgstr "اضغط لاغلاق الصفحة"
#: pretix/static/pretixcontrol/js/ui/main.js:1054
#: pretix/static/pretixcontrol/js/ui/main.js:1041
msgid "You have unsaved changes!"
msgstr "لم تقم بحفظ التعديلات!"
@@ -657,6 +632,16 @@ msgstr "غير ذلك"
msgid "Count"
msgstr "احسب"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr "نعم"
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr "لا"
#: pretix/static/pretixcontrol/js/ui/subevent.js:111
msgid "(one more date)"
msgid_plural "({num} more dates)"
File diff suppressed because it is too large Load Diff
+24 -39
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-28 14:51+0000\n"
"POT-Creation-Date: 2023-10-18 07:26+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-"
@@ -130,19 +130,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:164
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:225
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:213
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:246
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:264
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:254
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:247
msgid "Payment method unavailable"
msgstr ""
@@ -160,15 +159,15 @@ msgstr ""
msgid "Total revenue"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:14
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
msgid "Contacting Stripe …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:71
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
msgid "Total"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:220
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
msgid "Contacting your bank …"
msgstr ""
@@ -303,33 +302,17 @@ msgid "Ticket code is ambiguous on list"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order not approved"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Checked-in Tickets"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Valid Tickets"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Currently inside"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:69
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:70
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr ""
#: pretix/static/lightbox/js/lightbox.js:96
msgid "close"
msgstr ""
@@ -600,23 +583,15 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:778
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:781
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:939
#: pretix/static/pretixcontrol/js/ui/main.js:926
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:979
#: pretix/static/pretixcontrol/js/ui/main.js:966
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1054
#: pretix/static/pretixcontrol/js/ui/main.js:1041
msgid "You have unsaved changes!"
msgstr ""
@@ -632,6 +607,16 @@ msgstr ""
msgid "Count"
msgstr "Quantitat"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/subevent.js:111
msgid "(one more date)"
msgid_plural "({num} more dates)"
File diff suppressed because it is too large Load Diff
+24 -39
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-28 14:51+0000\n"
"POT-Creation-Date: 2023-10-18 07:26+0000\n"
"PO-Revision-Date: 2023-09-15 06:00+0000\n"
"Last-Translator: Michael <michael.happl@gmx.at>\n"
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -130,19 +130,18 @@ msgstr "WeChat Pay"
msgid "Mercado Pago"
msgstr "Mercado Pago"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:164
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "Pokračovat"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:225
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:213
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:246
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:264
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
msgid "Confirming your payment …"
msgstr "Potvrzuji vaši platbu …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:254
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:247
msgid "Payment method unavailable"
msgstr "Způsob platby není k dispozici"
@@ -160,15 +159,15 @@ msgstr "Zaplacené objednávky"
msgid "Total revenue"
msgstr "Celkové příjmy"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:14
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
msgid "Contacting Stripe …"
msgstr "Kontaktuji Stripe …"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:71
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
msgid "Total"
msgstr "Celkem"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:220
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
msgid "Contacting your bank …"
msgstr "Kontaktuji vaši banku …"
@@ -303,33 +302,17 @@ msgid "Ticket code is ambiguous on list"
msgstr "Kód vstupenky je v seznamu nejednoznačný"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order not approved"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Checked-in Tickets"
msgstr "Vyřízené vstupenky"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Valid Tickets"
msgstr "Platné vstupenky"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Currently inside"
msgstr "Aktuálně uvnitř"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:69
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr "Ano"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:70
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr "Ne"
#: pretix/static/lightbox/js/lightbox.js:96
msgid "close"
msgstr "zavřít"
@@ -619,23 +602,15 @@ msgstr "Žádný"
msgid "Selected only"
msgstr "Pouze vybrané"
#: pretix/static/pretixcontrol/js/ui/main.js:778
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:781
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:939
#: pretix/static/pretixcontrol/js/ui/main.js:926
msgid "Use a different name internally"
msgstr "Interně používat jiný název"
#: pretix/static/pretixcontrol/js/ui/main.js:979
#: pretix/static/pretixcontrol/js/ui/main.js:966
msgid "Click to close"
msgstr "Kliknutím zavřete"
#: pretix/static/pretixcontrol/js/ui/main.js:1054
#: pretix/static/pretixcontrol/js/ui/main.js:1041
msgid "You have unsaved changes!"
msgstr "Máte neuložené změny!"
@@ -651,6 +626,16 @@ msgstr "Další"
msgid "Count"
msgstr "Počet"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr "Ano"
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr "Ne"
#: pretix/static/pretixcontrol/js/ui/subevent.js:111
msgid "(one more date)"
msgid_plural "({num} more dates)"
File diff suppressed because it is too large Load Diff
+24 -39
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-28 14:51+0000\n"
"POT-Creation-Date: 2023-10-18 07:26+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"
@@ -130,19 +130,18 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:164
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:225
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:213
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:246
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:264
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
msgid "Confirming your payment …"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:254
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:247
msgid "Payment method unavailable"
msgstr ""
@@ -160,15 +159,15 @@ msgstr ""
msgid "Total revenue"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:14
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
msgid "Contacting Stripe …"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:71
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
msgid "Total"
msgstr ""
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:220
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
msgid "Contacting your bank …"
msgstr ""
@@ -303,33 +302,17 @@ msgid "Ticket code is ambiguous on list"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order not approved"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Checked-in Tickets"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Valid Tickets"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Currently inside"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:69
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:70
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr ""
#: pretix/static/lightbox/js/lightbox.js:96
msgid "close"
msgstr ""
@@ -600,23 +583,15 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:778
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:781
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:939
#: pretix/static/pretixcontrol/js/ui/main.js:926
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:979
#: pretix/static/pretixcontrol/js/ui/main.js:966
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1054
#: pretix/static/pretixcontrol/js/ui/main.js:1041
msgid "You have unsaved changes!"
msgstr ""
@@ -632,6 +607,16 @@ msgstr ""
msgid "Count"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/subevent.js:111
msgid "(one more date)"
msgid_plural "({num} more dates)"
File diff suppressed because it is too large Load Diff
+24 -39
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-28 14:51+0000\n"
"POT-Creation-Date: 2023-10-18 07:26+0000\n"
"PO-Revision-Date: 2022-12-01 17:00+0000\n"
"Last-Translator: Mie Frydensbjerg <mif@aarhus.dk>\n"
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -131,7 +131,7 @@ msgstr ""
msgid "Mercado Pago"
msgstr ""
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:164
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
#, fuzzy
#| msgctxt "widget"
@@ -139,14 +139,13 @@ msgstr ""
msgid "Continue"
msgstr "Fortsæt"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:225
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:213
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:246
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:264
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
msgid "Confirming your payment …"
msgstr "Bekræfter din betaling …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:254
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:247
msgid "Payment method unavailable"
msgstr "Betalingsmetode er ikke tilgængelig"
@@ -164,15 +163,15 @@ msgstr "Betalte bestillinger"
msgid "Total revenue"
msgstr "Omsætning i alt"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:14
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
msgid "Contacting Stripe …"
msgstr "Kontakter Stripe …"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:71
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
msgid "Total"
msgstr "Total"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:220
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
msgid "Contacting your bank …"
msgstr "Kontakter din bank …"
@@ -310,35 +309,19 @@ msgid "Ticket code is ambiguous on list"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order not approved"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
#, fuzzy
#| msgid "Check-in QR"
msgid "Checked-in Tickets"
msgstr "Check-in QR"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Valid Tickets"
msgstr "Gyldige billetter"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Currently inside"
msgstr "Inde i øjeblikket"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:69
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr "Ja"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:70
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr "Nej"
#: pretix/static/lightbox/js/lightbox.js:96
#, fuzzy
#| msgctxt "widget"
@@ -639,23 +622,15 @@ msgstr "Ingen"
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:778
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:781
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:939
#: pretix/static/pretixcontrol/js/ui/main.js:926
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:979
#: pretix/static/pretixcontrol/js/ui/main.js:966
msgid "Click to close"
msgstr "Klik for at lukke"
#: pretix/static/pretixcontrol/js/ui/main.js:1054
#: pretix/static/pretixcontrol/js/ui/main.js:1041
msgid "You have unsaved changes!"
msgstr "Du har ændringer, der ikke er gemt!"
@@ -673,6 +648,16 @@ msgstr "Andre"
msgid "Count"
msgstr "Antal"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr "Ja"
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr "Nej"
#: pretix/static/pretixcontrol/js/ui/subevent.js:111
msgid "(one more date)"
msgid_plural "({num} more dates)"
File diff suppressed because it is too large Load Diff
+30 -42
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-28 14:51+0000\n"
"PO-Revision-Date: 2023-11-28 15:36+0000\n"
"POT-Creation-Date: 2023-10-18 07:26+0000\n"
"PO-Revision-Date: 2023-09-25 23:46+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
"de/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.2.1\n"
"X-Generator: Weblate 5.0.2\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -130,19 +130,18 @@ msgstr "WeChat Pay"
msgid "Mercado Pago"
msgstr "Mercado Pago"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:164
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Continue"
msgstr "Fortfahren"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:225
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:213
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:246
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:264
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
msgid "Confirming your payment …"
msgstr "Zahlung wird bestätigt …"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:254
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:247
msgid "Payment method unavailable"
msgstr "Zahlungsmethode nicht verfügbar"
@@ -160,15 +159,15 @@ msgstr "Bezahlte Bestellungen"
msgid "Total revenue"
msgstr "Gesamtumsatz"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:14
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
msgid "Contacting Stripe …"
msgstr "Kontaktiere Stripe …"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:71
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
msgid "Total"
msgstr "Gesamt"
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:220
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
msgid "Contacting your bank …"
msgstr "Kontaktiere Ihre Bank …"
@@ -303,33 +302,17 @@ msgid "Ticket code is ambiguous on list"
msgstr "Ticket-Code ist nicht eindeutig auf der Liste"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order not approved"
msgstr "Bestellung nicht freigegeben"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Checked-in Tickets"
msgstr "Eingecheckte Tickets"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Valid Tickets"
msgstr "Gültige Tickets"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Currently inside"
msgstr "Derzeit anwesend"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:69
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr "Ja"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:70
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr "Nein"
#: pretix/static/lightbox/js/lightbox.js:96
msgid "close"
msgstr "schließen"
@@ -621,23 +604,15 @@ msgstr "Keine"
msgid "Selected only"
msgstr "Nur ausgewählte"
#: pretix/static/pretixcontrol/js/ui/main.js:778
msgid "Enter page number between 1 and %(max)s."
msgstr "Geben Sie eine Seitenzahl zwischen 1 und %(max)s ein."
#: pretix/static/pretixcontrol/js/ui/main.js:781
msgid "Invalid page number."
msgstr "Ungültige Seitenzahl."
#: pretix/static/pretixcontrol/js/ui/main.js:939
#: pretix/static/pretixcontrol/js/ui/main.js:926
msgid "Use a different name internally"
msgstr "Intern einen anderen Namen verwenden"
#: pretix/static/pretixcontrol/js/ui/main.js:979
#: pretix/static/pretixcontrol/js/ui/main.js:966
msgid "Click to close"
msgstr "Klicken zum Schließen"
#: pretix/static/pretixcontrol/js/ui/main.js:1054
#: pretix/static/pretixcontrol/js/ui/main.js:1041
msgid "You have unsaved changes!"
msgstr "Sie haben ungespeicherte Änderungen!"
@@ -653,6 +628,16 @@ msgstr "Sonstige"
msgid "Count"
msgstr "Anzahl"
#: pretix/static/pretixcontrol/js/ui/question.js:137
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "Yes"
msgstr "Ja"
#: pretix/static/pretixcontrol/js/ui/question.js:138
#: pretix/static/pretixpresale/js/ui/questions.js:270
msgid "No"
msgstr "Nein"
#: pretix/static/pretixcontrol/js/ui/subevent.js:111
msgid "(one more date)"
msgid_plural "({num} more dates)"
@@ -734,9 +719,12 @@ msgid "Price"
msgstr "Preis"
#: pretix/static/pretixpresale/js/widget/widget.js:20
#, fuzzy
#| msgctxt "widget"
#| msgid "Select %s"
msgctxt "widget"
msgid "Select"
msgstr "Auswählen"
msgstr "%s auswählen"
#: pretix/static/pretixpresale/js/widget/widget.js:21
#, javascript-format
-4
View File
@@ -6,7 +6,6 @@ Absenderinformation
Absendername
Admin
Adminbereich
Affirm
AGPL
AGPLv
Alipay
@@ -60,10 +59,8 @@ Branding
Browsereinstellungen
BSD
bspw
Bokmål
Boleto
Bundles
Butterfly
bzw
ca
Cc
@@ -341,7 +338,6 @@ URIs
Ursprüngl
USt
Überweisungs
Überzahlten
Validierung
Venmo
Veranstaltereinstellungen
File diff suppressed because it is too large Load Diff

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