Compare commits

..

4 Commits

Author SHA1 Message Date
Raphael Michel
bc64109e11 Fix Pillow version 2021-03-14 12:26:41 +01:00
Raphael Michel
40018b0937 more stuff 2021-03-11 23:11:31 +01:00
Raphael Michel
cffcddaf26 Fix deprecation warnings 2021-03-11 22:47:44 +01:00
Raphael Michel
bd70a2e7bf Some changes 2021-03-11 22:47:44 +01:00
861 changed files with 132974 additions and 248686 deletions

View File

@@ -1,5 +0,0 @@
{
"contributors": "https://crm.rami.io/cla/check/?project=pretix&checkContributor=",
"message": "Hey there! :) Thank you very much for offering a contribution to pretix! For legal reasons, we need you to sign a Contributor License Agreement in order to be able to merge the code. Sorry for the hassle :( Please download the agreement from https://pretix.eu/about/en/cla and send a signed copy to support@pretix.eu. Feel free to also contact us there or via comments here if you have any questions!",
"label": "cla-signed"
}

1
.gitattributes vendored
View File

@@ -6,7 +6,6 @@ src/pretix/static/datetimepicker/* linguist-vendored
src/pretix/static/colorpicker/* linguist-vendored
src/pretix/static/fileupload/* linguist-vendored
src/pretix/static/vuejs/* linguist-vendored
src/pretix/static/d3/* linguist-vendored
src/pretix/static/select2/* linguist-vendored
src/pretix/static/charts/* linguist-vendored
src/pretix/static/rrule/* linguist-vendored

View File

@@ -53,20 +53,3 @@ jobs:
- name: Run flake8
run: flake8 .
working-directory: ./src
licenseheader:
name: licenseheaders
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Install Dependencies
run: pip3 install licenseheaders
- name: Run licenseheaders
run: licenseheaders -t ../.licenseheader -E .py -x "*/migrations/*.py"
working-directory: ./src
- name: Check for changes
run: git diff --exit-code
working-directory: ./src

View File

@@ -61,9 +61,6 @@ jobs:
- name: Run checks
run: python manage.py check
working-directory: ./src
- name: Install JS dependencies
working-directory: ./src
run: make npminstall
- name: Compile
working-directory: ./src
run: make all compress

View File

@@ -28,7 +28,6 @@ pypi:
- python -m pretix migrate
- python -m pretix check
- check-manifest
- make npminstall
- python setup.py sdist bdist_wheel
- twine check dist/*
- twine upload dist/*

View File

@@ -1,19 +0,0 @@
This file is part of pretix (Community Edition).
Copyright (C) 2014-2020 Raphael Michel and contributors
Copyright (C) 2020-2021 rami.io GmbH and contributors
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
Public License as published by the Free Software Foundation in version 3 of the License.
ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
applicable granting you additional permissions and placing additional restrictions on your usage of this software.
Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
this file, see <https://pretix.eu/about/en/license>.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see
<https://www.gnu.org/licenses/>.

37
AUTHORS Normal file
View File

@@ -0,0 +1,37 @@
Here is an inevitably incomplete list of much-appreciated contributors --
people who have submitted patches, reported bugs, added translations, helped
answer newbie questions, improved the documentation, and generally made pretix
an awesome project. Thank you all!
Adam K. Sumner <asumner101@gmail.com>
Ahrdie <robert.deppe@me.com>
Alexander Brock <Brock.Alexander@web.de>
Brandon Pineda
Bolutife Lawrence
Christian Franke <nobody@nowhere.ws>
Christopher Dambamuromo <me@chridam.com>
chotee <chotee@openended.eu>
Cpt. Foo
Daniel Rosenblüh
Enrique Saez
Flavia Bastos
informancer <informancer@web.de>
Jakob Schnell <github@ezelo.de>
Jan Felix Wiebe <git@jfwie.be>
Jan Weiß
Jason Estibeiro <jasonestibeiro@live.com>
jlwt90
Jonas Große Sundrup <cherti@letopolis.de>
Kevin Nelson
Leah Oswald
Lukas Martini
Nathan Mattes
Nicole Klünder
Marc-Pascal Clement
Martin Gross <martin@pc-coholic.de>
Raphael Michel <mail@raphaelmichel.de>
Team MRMCD
Tobias Kunze <rixx@cutebit.de>
Oliver Knapp <github@oliverknapp.de>
Vishal Sodani <vishalsodani@rediffmail.com>
Jan Felix Wiebe <git@jfwie.be>

View File

@@ -3,9 +3,9 @@ Contributing to pretix
Hey there and welcome to pretix!
* We've got a contributors guide in [our documentation](https://docs.pretix.eu/en/latest/development/contribution/) together with notes on the [development setup](https://docs.pretix.eu/en/latest/development/setup.html).
We've got a contributors guide in [our documentation](https://docs.pretix.eu/en/latest/development/contribution/)
together with notes on the [development setup](https://docs.pretix.eu/en/latest/development/setup.html).
* Please note that we have a [Code of Conduct](https://docs.pretix.eu/en/latest/development/contribution/codeofconduct.html) in place that applies to all project contributions, including issues, pull requests, etc.
* Before we can accept a PR from you we'll need you to sign [our CLA](https://pretix.eu/about/en/cla). You can find more information about the how and why in our [License FAQ](https://docs.pretix.eu/en/latest/license/faq.html#) and in our [license change blog post](https://pretix.eu/about/en/blog/20210412-license/).
Please note that we have a [Code of Conduct](https://docs.pretix.eu/en/latest/development/contribution/codeofconduct.html)
in place that applies to all project contributions, including issues, pull requests, etc.

32
COPYRIGHT Normal file
View File

@@ -0,0 +1,32 @@
Copyright 2014-2016 Raphael Michel
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License 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.
This project includes the work of others, namely:
* Django, (c) Django Software Foundation and contributors, BSD License
* Font Awesome, (c) Dave Gandy, SIL Open Font License and MIT License
* Bootstrap, (c) Twitter, Inc., MIT License
* jQuery, (c) jQuery Foundation and contributors, MIT License
* django-formset-js, (c) Ionata Web Solutions, BSD License
* CleanerVersion, (c) Jean-Christophe Zulian, Brian King, Andrea Marcacci, Manuel Jeckelmann, Apache License
* django-bootstrap3, (c) Dylan Verheul, Apache License
* pytz, (c) Stuart Bishop, MIT License
* python-dateutil, (c) Yaron de Leeuw, BSD License
* startbootstrap-sb-admin-2, (c) Iron Summit Media Strategies, LLC, Apache License
* metismenu, (c) Osman Nuri Okumus, MIT License
* easy-thumbnails, (c) Chris Beaven and contributors
* reportlab, (c) ReportLab Europe Ltd, BSD License
* django-compressor, (c) Jannis Leidel and contributors, MIT License
* static3, (c) Roman Mohr and contributors, LGPL License
* Lightbox, (c) Lokesh Dhakar, MIT License

View File

@@ -31,11 +31,7 @@ RUN apt-get update && \
useradd -ms /bin/bash -d /pretix -u 15371 pretixuser && \
echo 'pretixuser ALL=(ALL) NOPASSWD:SETENV: /usr/bin/supervisord' >> /etc/sudoers && \
mkdir /static && \
mkdir /etc/supervisord && \
curl -fsSL https://deb.nodesource.com/setup_15.x | sudo -E bash - && \
apt-get install -y nodejs && \
curl -qL https://www.npmjs.com/install.sh | sh
mkdir /etc/supervisord
ENV LC_ALL=C.UTF-8 \
DJANGO_SETTINGS_MODULE=production_settings

1118
LICENSE

File diff suppressed because it is too large Load Diff

View File

@@ -52,9 +52,11 @@ including issues, pull requests, etc.
License
-------
The code in this repository is published under the terms of the Apache License.
See the LICENSE file for the complete license text.
The code in this repository is covered by different licenses. Most of it is available to everyone under the terms of
the GNU AGPL license v3 with additional terms. See the LICENSE file for the complete license details.
This project is maintained by Raphael Michel. See the AUTHORS file for a list of all
the awesome folks who contributed to this project.
.. _installation guide: https://docs.pretix.eu/en/latest/admin/installation/index.html
.. _developer documentation: https://docs.pretix.eu/en/latest/development/index.html

View File

@@ -33,7 +33,7 @@ http {
gzip on;
gzip_disable "msie6";
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/javascript text/xml application/xml application/rss+xml application/atom+xml application/rdf+xml image/svg+xml;
gzip_types text/plain text/html text/css application/json application/javascript application/x-javascript text/javascript text/xml application/xml application/rss+xml application/atom+xml application/rdf+xml image/svg+xml;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;

View File

@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License 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.

View File

@@ -350,18 +350,10 @@ application. If you want to use sentry, you need to set a DSN in the configurati
[sentry]
dsn=https://<key>:<secret>@sentry.io/<project>
traces_sample_rate=0.5
traces_sample_token=xyz
``dsn``
You will be given this value by your sentry installation.
``traces_sample_rate``
Sample rate for performance monitoring.
``traces_sample_token``
If this token is found in a query string, a trace will always be sampled.
Caching
-------

View File

@@ -45,8 +45,6 @@ Here is the currently recommended set of commands::
CREATE INDEX CONCURRENTLY pretix_addidx_order_comment
ON pretixbase_order
USING gin (upper("comment") gin_trgm_ops);
CREATE INDEX CONCURRENTLY pretix_addidx_order_event_date
ON public.pretixbase_order (event_id, datetime);
CREATE INDEX CONCURRENTLY pretix_addidx_orderpos_name
ON pretixbase_orderposition
USING gin (upper("attendee_name_cached") gin_trgm_ops);
@@ -62,14 +60,7 @@ Here is the currently recommended set of commands::
CREATE INDEX CONCURRENTLY pretix_addidx_ia_company
ON pretixbase_invoiceaddress
USING gin (upper("company") gin_trgm_ops);
CREATE INDEX CONCURRENTLY pretix_addidx_orderpos_email_upper
ON public.pretixbase_orderposition (upper((attendee_email)::text));
CREATE INDEX CONCURRENTLY pretix_addidx_voucher_code_upper
ON public.pretixbase_voucher (upper((code)::text));
CREATE INDEX CONCURRENTLY pretix_addidx_logentry_event_date
ON public.pretixbase_logentry (event_id, datetime);
CREATE INDEX CONCURRENTLY pretix_addidx_logentry_event_cid_date
ON public.pretixbase_logentry (event_id, content_type_id, datetime);
CREATE INDEX CONCURRENTLY pretix_addidx_orderpos_email_upper ON public.pretixbase_orderposition (upper((attendee_email)::text));
Also, if you use our ``pretix-shipping`` plugin::

View File

@@ -26,7 +26,7 @@ installation guides):
* `Docker`_
* A SMTP server to send out mails, e.g. `Postfix`_ on your machine or some third-party server you have credentials for
* A HTTP reverse proxy, e.g. `nginx`_ or Apache to allow HTTPS connections
* A `PostgreSQL`_ 9.6+, `MySQL`_ 5.7+, or MariaDB 10.2.7+ database server
* A `PostgreSQL`_ 9.5+, `MySQL`_ 5.7+, or MariaDB 10.2.7+ database server
* A `redis`_ server
We also recommend that you use a firewall, although this is not a pretix-specific recommendation. If you're new to

View File

@@ -23,9 +23,8 @@ installation guides):
* A SMTP server to send out mails, e.g. `Postfix`_ on your machine or some third-party server you have credentials for
* A HTTP reverse proxy, e.g. `nginx`_ or Apache to allow HTTPS connections
* A `PostgreSQL`_ 9.6+, `MySQL`_ 5.7+, or MariaDB 10.2.7+ database server
* A `PostgreSQL`_ 9.5+, `MySQL`_ 5.7+, or MariaDB 10.2.7+ database server
* A `redis`_ server
* A `nodejs_` installation
We also recommend that you use a firewall, although this is not a pretix-specific recommendation. If you're new to
Linux and firewalls, we recommend that you start with `ufw`_.
@@ -313,4 +312,3 @@ example::
.. _redis: https://blog.programster.org/debian-8-install-redis-server/
.. _ufw: https://en.wikipedia.org/wiki/Uncomplicated_Firewall
.. _strong encryption settings: https://mozilla.github.io/server-side-tls/ssl-config-generator/
.. _nodejs: https://github.com/nodesource/distributions/blob/master/README.md#deb

View File

@@ -213,16 +213,13 @@ Please note that this also goes for most error responses. For example, if we ret
error and you retry with the same ``X-Idempotency-Key``, you will get the same error again, even if you were granted
permission in the meantime! This includes internal server errors on our side that might have been fixed in the meantime.
There are only the following exceptions to the rule:
There are only three exceptions to the rule:
* Responses with status code ``409 Conflict`` are not cached. If you send the request again, it will be executed as a
new request, since these responses are intended to be retried.
* Rate-limited responses with status code ``429 Too Many Requests`` are not cached and you can safely retry them.
* Responses with status code ``500 Internal Server Error`` are not cached and you can retry them. This is not guaranteed
to be safe in all theoretical cases, but 500 by definition is an unforeseen situation and we need to have some way out.
* Responses with status code ``503 Service Unavailable`` are not cached and you can safely retry them.
If you send a request with an ``X-Idempotency-Key`` header that we have seen before but that has not yet received a

View File

@@ -49,10 +49,6 @@ exit_all_at datetime Automatically c
The ``exit_all_at`` attribute has been added.
.. versionchanged:: 3.17
The ``ends_after`` and ``expand`` query parameters have been added.
Endpoints
---------
@@ -104,8 +100,6 @@ Endpoints
:query integer page: The page number in case of a multi-page result set, default is 1
:query integer subevent: Only return check-in lists of the sub-event with the given ID
:query integer subevent_match: Only return check-in lists that are valid for the sub-event with the given ID (i.e. also lists valid for all subevents)
:query string ends_after: Exclude all check-in lists attached to a sub-event that is already in the past at the given time.
:query string expand: Expand a field into a full object. Currently only ``subevent`` is supported. Can be passed multiple times.
:query string exclude: Exclude a field from the output, e.g. ``checkin_count``. Can be used as a performance optimization. Can be passed multiple times.
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
@@ -453,7 +447,6 @@ 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 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.
@@ -494,6 +487,8 @@ Order position endpoints
* If ``attendee_name`` is empty, it will automatically fall back to values from a parent product or from invoice
addresses.
**Instead of an ID, you can also use the ``secret`` field as the lookup parameter.**
**Example request**:
.. sourcecode:: http
@@ -567,7 +562,7 @@ Order position endpoints
Tries to redeem an order position, identified by its internal ID, i.e. checks the attendee in. This endpoint
accepts a number of optional requests in the body.
**Tip:** Instead of an ID, you can also use the ``secret`` field as the lookup parameter.
**Instead of an ID, you can also use the ``secret`` field as the lookup parameter.**
:<json boolean questions_supported: When this parameter is set to ``true``, handling of questions is supported. If
you do not implement question handling in your user interface, you **must**
@@ -695,9 +690,6 @@ Order position endpoints
* ``product`` - Tickets with this product may not be scanned at this device
* ``rules`` - Check-in prevented by a user-defined rule
In case of reason ``rules``, 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``.
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param list: The ID of the check-in list to look for

View File

@@ -1,238 +0,0 @@
.. _`rest-customers`:
Customers
=========
Resource description
--------------------
The customer resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
identifier string Internal ID of the customer
email string Customer email address
name string Name of this customer (or ``null``)
name_parts object of strings Decomposition of name (i.e. given name, family name)
is_active boolean Whether this account is active
is_verified boolean Whether the email address of this account has been
verified
last_login datetime Date and time of last login
date_joined datetime Date and time of registration
locale string Preferred language of the customer
last_modified datetime Date and time of modification of the record
===================================== ========================== =======================================================
.. versionadded:: 4.0
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/customers/
Returns a list of all customers registered with a given organizer.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/customers/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"identifier": "8WSAJCJ",
"email": "customer@example.org",
"name": "John Doe",
"name_parts": {
"_scheme": "full",
"full_name": "John Doe"
},
"is_active": true,
"is_verified": false,
"last_login": null,
"date_joined": "2021-04-06T13:44:22.809216Z",
"locale": "de",
"last_modified": "2021-04-06T13:44:22.809377Z"
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string email: Only fetch customers with this email address
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/customers/(identifier)/
Returns information on one customer, identified by its identifier.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/customers/8WSAJCJ/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"identifier": "8WSAJCJ",
"email": "customer@example.org",
"name": "John Doe",
"name_parts": {
"_scheme": "full",
"full_name": "John Doe"
},
"is_active": true,
"is_verified": false,
"last_login": null,
"date_joined": "2021-04-06T13:44:22.809216Z",
"locale": "de",
"last_modified": "2021-04-06T13:44:22.809377Z"
}
:param organizer: The ``slug`` field of the organizer to fetch
:param identifier: The ``identifier`` field of the customer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/customers/
Creates a new customer
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/customers/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"email": "test@example.org"
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"identifier": "8WSAJCJ",
"email": "test@example.org",
...
}
:param organizer: The ``slug`` field of the organizer to create a customer for
:statuscode 201: no error
:statuscode 400: The customer could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/customers/(identifier)/
Update a customer. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``identifier``, ``last_login``, ``date_joined``, ``name``,
and ``last_modified`` fields.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/customers/8WSAJCJ/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"email": "test@example.org"
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"identifier": "8WSAJCJ",
"email": "test@example.org",
}
:param organizer: The ``slug`` field of the organizer to modify
:param identifier: The ``identifier`` field of the customer to modify
:statuscode 200: no error
:statuscode 400: The customer could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource.
.. http:post:: /api/v1/organizers/(organizer)/customers/(identifier)/anonymize/
Anonymize a customer. Deletes personal data and disconnects from existing orders.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/customers/8WSAJCJ/anonymize/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"identifier": "8WSAJCJ",
"email": null,
}
:param organizer: The ``slug`` field of the organizer to modify
:param identifier: The ``identifier`` field of the customer to modify
:statuscode 200: no error
:statuscode 400: The customer could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource.

View File

@@ -80,10 +80,6 @@ Endpoints
The events resource can now be filtered by meta data attributes.
.. versionchanged:: 4.0
The ``clone_from`` parameter has been added to the event creation endpoint.
.. http:get:: /api/v1/organizers/(organizer)/events/
Returns a list of all events within a given organizer the authenticated user/token has access to.
@@ -325,9 +321,6 @@ Endpoints
}
:param organizer: The ``slug`` field of the organizer of the event to create.
:query clone_from: Set to ``event_slug`` to clone data (settings, products, …) from an event with this slug in the
same organizer or to ``organizer_slug/event_slug`` to clone from an event within a different
organizer.
:statuscode 201: no error
:statuscode 400: The event could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
@@ -342,8 +335,7 @@ Endpoints
If the ``plugins``, ``has_subevents`` and/or ``is_public`` fields are present in the post body this will determine their
value. Otherwise their value will be copied from the existing event.
Please note that you can only copy from events under the same organizer this way. Use the ``clone_from`` parameter
when creating a new event for this instead.
Please note that you can only copy from events under the same organizer.
Permission required: "Can create events"

View File

@@ -21,9 +21,6 @@ Resources and endpoints
vouchers
checkinlists
waitinglist
customers
membershiptypes
memberships
giftcards
carts
teams

View File

@@ -24,8 +24,6 @@ active boolean If ``false``, t
description multi-lingual string A public description of the variation. May contain
Markdown syntax or can be ``null``.
position integer An integer, used for sorting
require_membership boolean If ``true``, booking this variation requires an active membership.
require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
===================================== ========================== =======================================================
Endpoints
@@ -62,8 +60,6 @@ Endpoints
"en": "S"
},
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": {
"en": "Test2"
},
@@ -78,8 +74,6 @@ Endpoints
"en": "L"
},
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": {},
"position": 1,
"default_price": null,
@@ -127,8 +121,6 @@ Endpoints
"price": "10.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 0
}
@@ -158,8 +150,6 @@ Endpoints
"value": {"en": "Student"},
"default_price": "10.00",
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 0
}
@@ -179,8 +169,6 @@ Endpoints
"price": "10.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 0
}
@@ -231,8 +219,6 @@ Endpoints
"price": "10.00",
"original_price": null,
"active": false,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 1
}

View File

@@ -69,16 +69,6 @@ require_approval boolean If ``true``, or
approved by the event organizer before they can be
paid.
require_bundling boolean If ``true``, this item is only available as part of bundles.
require_membership boolean If ``true``, booking this item requires an active membership.
require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
grant_membership_type integer If set to the internal ID of a membership type, purchasing this item will
create a membership of the given type.
grant_membership_duration_like_event boolean If ``true``, the membership created through ``grant_membership_type`` will derive
its term from ``date_from`` to ``date_to`` of the purchased (sub)event.
grant_membership_duration_days integer If ``grant_membership_duration_like_event`` is ``false``, this sets the number of
days for the membership.
grant_membership_duration_months integer If ``grant_membership_duration_like_event`` is ``false``, this sets the number of
calendar months for the membership.
generate_tickets boolean If ``false``, tickets are never generated for this
product, regardless of other settings. If ``true``,
tickets are generated even if this is a
@@ -104,8 +94,6 @@ variations list of objects A list with one
for price calculations (or ``null``).
├ active boolean If ``false``, this variation will not be sold or shown.
├ description multi-lingual string A public description of the variation. May contain
├ require_membership boolean If ``true``, booking this variation requires an active membership.
├ require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
Markdown syntax or can be ``null``.
└ position integer An integer, used for sorting
addons list of objects Definition of add-ons that can be chosen for this item.
@@ -138,11 +126,6 @@ meta_data object Values set for
The attribute ``multi_allowed`` has been added to ``addons``.
.. versionchanged:: 4.0
The attributes ``require_membership``, ``require_membership_types``, ``grant_membership_type``, ``grant_membership_duration_like_event``,
``grant_membership_duration_days`` and ``grant_membership_duration_months`` have been added.
Notes
-----
@@ -215,12 +198,6 @@ Endpoints
"show_quota_left": null,
"require_approval": false,
"require_bundling": false,
"require_membership": false,
"require_membership_types": [],
"grant_membership_type": null,
"grant_membership_duration_like_event": true,
"grant_membership_duration_days": 0,
"grant_membership_duration_months": 0,
"variations": [
{
"value": {"en": "Student"},
@@ -228,8 +205,6 @@ Endpoints
"price": "10.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 0
},
@@ -239,8 +214,6 @@ Endpoints
"price": "23.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 1
}
@@ -321,12 +294,6 @@ Endpoints
"has_variations": false,
"require_approval": false,
"require_bundling": false,
"require_membership": false,
"require_membership_types": [],
"grant_membership_type": null,
"grant_membership_duration_like_event": true,
"grant_membership_duration_days": 0,
"grant_membership_duration_months": 0,
"variations": [
{
"value": {"en": "Student"},
@@ -334,8 +301,6 @@ Endpoints
"price": "10.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 0
},
@@ -345,8 +310,6 @@ Endpoints
"price": "23.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 1
}
@@ -407,12 +370,6 @@ Endpoints
"checkin_attention": false,
"require_approval": false,
"require_bundling": false,
"require_membership": false,
"require_membership_types": [],
"grant_membership_type": null,
"grant_membership_duration_like_event": true,
"grant_membership_duration_days": 0,
"grant_membership_duration_months": 0,
"variations": [
{
"value": {"en": "Student"},
@@ -420,8 +377,6 @@ Endpoints
"price": "10.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 0
},
@@ -431,8 +386,6 @@ Endpoints
"price": "23.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 1
}
@@ -482,12 +435,6 @@ Endpoints
"has_variations": true,
"require_approval": false,
"require_bundling": false,
"require_membership": false,
"require_membership_types": [],
"grant_membership_type": null,
"grant_membership_duration_like_event": true,
"grant_membership_duration_days": 0,
"grant_membership_duration_months": 0,
"variations": [
{
"value": {"en": "Student"},
@@ -495,8 +442,6 @@ Endpoints
"price": "10.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 0
},
@@ -506,8 +451,6 @@ Endpoints
"price": "23.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 1
}
@@ -588,12 +531,6 @@ Endpoints
"has_variations": true,
"require_approval": false,
"require_bundling": false,
"require_membership": false,
"require_membership_types": [],
"grant_membership_type": null,
"grant_membership_duration_like_event": true,
"grant_membership_duration_days": 0,
"grant_membership_duration_months": 0,
"variations": [
{
"value": {"en": "Student"},
@@ -601,8 +538,6 @@ Endpoints
"price": "10.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 0
},
@@ -612,8 +547,6 @@ Endpoints
"price": "23.00",
"original_price": null,
"active": true,
"require_membership": false,
"require_membership_types": [],
"description": null,
"position": 1
}

View File

@@ -1,216 +0,0 @@
Memberships
===========
Resource description
--------------------
The membership resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the membership
customer string Identifier of the customer associated with this membership (can't be changed)
testmode boolean Whether this is a test membership
membership_type integer Internal ID of the membership type
date_start datetime Start of validity
date_end datetime End of validity
attendee_name_parts object JSON representation of components of an attendee name (configuration dependent)
===================================== ========================== =======================================================
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/memberships/
Returns a list of all memberships within a given organizer.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/memberships/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 2,
"customer": "EGR9SYT",
"membership_type": 1,
"testmode": false,
"date_start": "2021-04-19T00:00:00+02:00",
"date_end": "2021-04-20T00:00:00+02:00",
"attendee_name_parts": {
"_scheme": "title_given_family",
"family_name": "Doe",
"given_name": "John",
"title": ""
}
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string customer: A customer identifier to filter for
:query integer membership_type: A membership type ID to filter for
:query boolean testmode: Filter for memberships that are (not) in test mode.
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/memberships/(id)/
Returns information on one membership, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/memberships/2/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 2,
"customer": "EGR9SYT",
"membership_type": 1,
"testmode": false,
"date_start": "2021-04-19T00:00:00+02:00",
"date_end": "2021-04-20T00:00:00+02:00",
"attendee_name_parts": {
"_scheme": "title_given_family",
"family_name": "Doe",
"given_name": "John",
"title": ""
}
}
:param organizer: The ``slug`` field of the organizer to fetch
:param id: The ``id`` field of the membership to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/memberships/
Creates a new membership
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/memberships/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"membership_type": 2,
"customer": "EGR9SYT",
"testmode": false,
"date_start": "2021-04-19T00:00:00+02:00",
"date_end": "2021-04-20T00:00:00+02:00",
"attendee_name_parts": {
"_scheme": "title_given_family",
"family_name": "Doe",
"given_name": "John",
"title": ""
}
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 3,
"membership_type": 2,
"customer": "EGR9SYT",
"testmode": false,
"date_start": "2021-04-19T00:00:00+02:00",
"date_end": "2021-04-20T00:00:00+02:00",
"attendee_name_parts": {
"_scheme": "title_given_family",
"family_name": "Doe",
"given_name": "John",
"title": ""
}
}
:param organizer: The ``slug`` field of the organizer to create a membership for
:statuscode 201: no error
:statuscode 400: The membership could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/memberships/(id)/
Update a membership. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``id``, ``customer``, and ``testmode`` fields.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/memberships/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"membership_type": 3
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"membership_type": 3,
}
:param organizer: The ``slug`` field of the organizer to modify
:param id: The ``id`` field of the membership to modify
:statuscode 200: no error
:statuscode 400: The membership could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource.

View File

@@ -1,227 +0,0 @@
Membership types
================
Resource description
--------------------
The membership type resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the membership type
name multi-lingual string Human-readable name of the type
transferable boolean Whether a membership of this type can be used by
multiple persons
allow_parallel_usage boolean Whether a membership of this type can be used for
multiple parallel tickets
max_usages integer Maximum number of times a membership of this type can be
used.
===================================== ========================== =======================================================
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/membershiptypes/
Returns a list of all membership types within a given organizer.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/membershiptypes/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 2,
"name": {
"de": "Wochenkarte",
"en": "Week pass"
},
"transferable": false,
"allow_parallel_usage": false,
"max_usages": 7
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/membershiptypes/(id)/
Returns information on one membership type, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/membershiptypes/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"name": {
"de": "Wochenkarte",
"en": "Week pass"
},
"transferable": false,
"allow_parallel_usage": false,
"max_usages": 7
}
:param organizer: The ``slug`` field of the organizer to fetch
:param id: The ``id`` field of the membership type to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/membershiptypes/
Creates a new membership type
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/membershiptypes/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"name": {
"de": "Wochenkarte",
"en": "Week pass"
},
"transferable": false,
"allow_parallel_usage": false,
"max_usages": 7
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 3,
"name": {
"de": "Wochenkarte",
"en": "Week pass"
},
"transferable": false,
"allow_parallel_usage": false,
"max_usages": 7
}
:param organizer: The ``slug`` field of the organizer to create a membership type for
:statuscode 201: no error
:statuscode 400: The membership type could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/membershiptypes/(id)/
Update a membership type. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``id`` field.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/membershiptypes/2/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"max_usages": 3
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 2,
"name": {
"de": "Wochenkarte",
"en": "Week pass"
},
"transferable": false,
"allow_parallel_usage": false,
"max_usages": 3
}
:param organizer: The ``slug`` field of the organizer to modify
:param id: The ``id`` field of the membership type to modify
:statuscode 200: no error
:statuscode 400: The membership could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource.
.. http:delete:: /api/v1/organizers/(organizer)/membershiptypes/(id)/
Delete a membership type. You can not delete types which have already been used.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/membershiptype/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param id: The ``id`` field of the type to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to delete this resource **or** the membership type is currently in use.

View File

@@ -31,7 +31,6 @@ testmode boolean If ``true``, th
secret string The secret contained in the link sent to the customer
email string The customer email address
phone string The customer phone number
customer string The customer account identifier (or ``null``)
locale string The locale used for communication with this customer
sales_channel string Channel this sale was created through, such as
``"web"``.
@@ -119,10 +118,6 @@ last_modified datetime Last modificati
The ``phone`` attribute has been added.
.. versionchanged:: 4.0
The ``customer`` attribute has been added.
.. _order-position-resource:
@@ -294,7 +289,6 @@ List of all orders
"url": "https://test.pretix.eu/dummy/dummy/order/ABC12/k24fiuwvu8kxz3y1/",
"email": "tester@example.org",
"phone": "+491234567",
"customer": null,
"locale": "en",
"sales_channel": "web",
"datetime": "2017-12-01T10:00:00Z",
@@ -463,7 +457,6 @@ Fetching individual orders
"url": "https://test.pretix.eu/dummy/dummy/order/ABC12/k24fiuwvu8kxz3y1/",
"email": "tester@example.org",
"phone": "+491234567",
"customer": null,
"locale": "en",
"sales_channel": "web",
"datetime": "2017-12-01T10:00:00Z",
@@ -782,8 +775,6 @@ Creating orders
* does not support redeeming gift cards
* does not support or validate memberships
You can supply the following fields of the resource:
* ``code`` (optional)
@@ -792,7 +783,6 @@ Creating orders
or in state ``confirmed``, depending on this value. If you create a paid order, the ``order_paid`` signal will
**not** be sent out to plugins and no email will be sent. If you want that behavior, create an unpaid order and
then call the ``mark_paid`` API method.
* ``customer`` (optional) Customer identifier or ``null``
* ``testmode`` (optional) Defaults to ``false``
* ``consume_carts`` (optional) A list of cart IDs. All cart positions with these IDs will be deleted if the
order creation is successful. Any quotas or seats that become free by this operation will be credited to your order
@@ -1639,8 +1629,7 @@ Manipulating individual positions
* ``answers``: If specified, you will need to provide **all** answers for this order position.
Validation is handled the same way as when creating orders through the API. You are therefore
expected to provide ``question``, ``answer``, and possibly ``options``. ``question_identifier``
and ``option_identifiers`` will be ignored. As a special case, you can submit the magic value
``"file:keep"`` as the answer to a file question to keep the current value without re-uploading it.
and ``option_identifiers`` will be ignored.
**Example request**:

View File

@@ -62,7 +62,6 @@ valid_date_min date Minimum value f
valid_date_max date Maximum value for date questions (optional)
valid_datetime_min datetime Minimum value for date and time questions (optional)
valid_datetime_max datetime Maximum value for date and time questions (optional)
valid_file_portrait boolean Turn on file validation for portrait photos
dependency_question integer Internal ID of a different question. The current
question will only be shown if the question given in
this attribute is set to the value given in
@@ -84,10 +83,6 @@ dependency_value string An old version
The attributes ``valid_*`` have been added.
.. versionchanged:: 3.18
The attribute ``valid_file_portrait`` have been added.
Endpoints
---------
@@ -139,7 +134,6 @@ Endpoints
"valid_date_max": null,
"valid_datetime_min": null,
"valid_datetime_max": null,
"valid_file_portrait": false,
"dependency_question": null,
"dependency_value": null,
"dependency_values": [],
@@ -217,7 +211,6 @@ Endpoints
"valid_date_max": null,
"valid_datetime_min": null,
"valid_datetime_max": null,
"valid_file_portrait": false,
"dependency_question": null,
"dependency_value": null,
"dependency_values": [],
@@ -318,7 +311,6 @@ Endpoints
"valid_date_max": null,
"valid_datetime_min": null,
"valid_datetime_max": null,
"valid_file_portrait": false,
"options": [
{
"id": 1,
@@ -400,7 +392,6 @@ Endpoints
"valid_date_max": null,
"valid_datetime_min": null,
"valid_datetime_max": null,
"valid_file_portrait": false,
"options": [
{
"id": 1,

View File

@@ -38,18 +38,14 @@ location multi-lingual string The sub-event l
geo_lat float Latitude of the location (or ``null``)
geo_lon float Longitude of the location (or ``null``)
item_price_overrides list of objects List of items for which this sub-event overrides the
default price or settings
default price
├ item integer The internal item ID
├ disabled boolean If ``true``, item should not be available for this sub-event
├ available_from datetime Start of availability (or ``null``)
├ available_until datetime End of availability (or ``null``)
└ price money (string) The price or ``null`` for the default price
variation_price_overrides list of objects List of variations for which this sub-event overrides
the default price or settings
the default price
├ variation integer The internal variation ID
├ disabled boolean If ``true``, variation should not be available for this sub-event
├ available_from datetime Start of availability (or ``null``)
├ available_until datetime End of availability (or ``null``)
└ price money (string) The price or ``null`` for the default price
meta_data object Values set for organizer-specific meta data parameters.
seating_plan integer If reserved seating is in use, the ID of a seating
@@ -71,10 +67,6 @@ last_modified datetime Last modificati
The ``last_modified`` attribute has been added.
.. versionchanged:: 3.18
The ``available_from``/``available_until`` attributes have been added to ``item_price_overrides`` and ``variation_price_overrides``.
Endpoints
---------
@@ -127,8 +119,6 @@ Endpoints
{
"item": 2,
"disabled": false,
"available_from": null,
"available_until": null,
"price": "12.00"
}
],
@@ -189,8 +179,6 @@ Endpoints
{
"item": 2,
"disabled": false,
"available_from": null,
"available_until": null,
"price": "12.00"
}
],
@@ -226,8 +214,6 @@ Endpoints
{
"item": 2,
"disabled": false,
"available_from": null,
"available_until": null,
"price": "12.00"
}
],
@@ -284,8 +270,6 @@ Endpoints
{
"item": 2,
"disabled": false,
"available_from": null,
"available_until": null,
"price": "12.00"
}
],
@@ -323,8 +307,6 @@ Endpoints
{
"item": 2,
"disabled": false,
"available_from": null,
"available_until": null,
"price": "23.42"
}
],
@@ -358,8 +340,6 @@ Endpoints
{
"item": 2,
"disabled": false,
"available_from": null,
"available_until": null,
"price": "23.42"
}
],
@@ -449,8 +429,6 @@ Endpoints
{
"item": 2,
"disabled": false,
"available_from": null,
"available_until": null,
"price": "12.00"
}
],

View File

@@ -1,4 +1,4 @@
.. spelling:: fullname checkin
.. spelling:: fullname
.. _`rest-teams`:
@@ -25,7 +25,6 @@ limit_events list List of event s
can_create_events boolean
can_change_teams boolean
can_change_organizer_settings boolean
can_manage_customers boolean
can_manage_gift_cards boolean
can_change_event_settings boolean
can_change_items boolean
@@ -33,7 +32,6 @@ can_view_orders boolean
can_change_orders boolean
can_view_vouchers boolean
can_change_vouchers boolean
can_checkin_orders boolean
===================================== ========================== =======================================================
Team member resource

View File

@@ -9,5 +9,4 @@ Table of contents
api/index
development/index
plugins/index
license/faq

View File

@@ -94,7 +94,7 @@ F. Functionality
#. Refunds are implemented, if possible.
#. In case of overpayment or external refunds, an external refund is properly created.
#. In case of overpayment or external refunds, a "required action" is created to notify the event organizer.
#. If the plugin adds steps to the checkout process, it has been tested in combination with the pretix widget.

View File

@@ -34,6 +34,9 @@ Organizers and events
.. autoclass:: pretix.base.models.TeamAPIToken
:members:
.. autoclass:: pretix.base.models.RequiredAction
:members:
.. autoclass:: pretix.base.models.EventMetaProperty
:members:

View File

@@ -67,10 +67,6 @@ Then, create the local database::
A first user with username ``admin@localhost`` and password ``admin`` will be automatically
created.
You will also need to install a few JavaScript dependencies::
make npminstall
If you want to see pretix in a different language than English, you have to compile our language
files::

View File

@@ -1,177 +0,0 @@
.. spelling::
AGPL
AGPLv3
GPL
LGPL
Apache
BSD
MIT
CLA
django
i18nfields
hierarkey
rami.io
rami
io
GmbH
License FAQ
===========
.. warning::
This FAQ tries to explain in simpler terms what the license of the pretix open source project does and does not
allow. It is based on our interpretation of the license and is not legal advice. The contents of this page are not
legally binding, only the original text of the license in the `license file`_ is legally binding.
How is pretix licensed?
-----------------------
pretix follows the popular dual licensing model. It is available under the `GNU Affero General Public License 3`_ (AGPL)
plus some additional terms, as well as under a proprietary license ("pretix Enterprise license") on request.
How can it be AGPL if there are additional terms?
-------------------------------------------------
Even though it is fairly unknown, the AGPL's section 7 is titled "Additional Terms" and outlines specific conditions
under which additional terms can be imposed on an AGPL-licensed work. In our case, we add three additional terms.
The first additional term for pretix is an additional **permission**. It allows you to do something that the AGPL would
generally not allow. As it doesn't restrict your freedoms granted by AGPL, if you don't like it, you can ignore it, and
if you distribute pretix further, you can remove it.
The second and third additional term for pretix are additional terms that restrict or specify other provisions of the
license. AGPL specifically requires that these terms can only restrict or specify very specific things and we believe
our additional terms are in compliance with that and are thus valid and may not be removed.
Why did you choose this license model?
--------------------------------------
pretix was born in the open source community and we're deeply committed to building the best open source ticketing
solution in the world. It is important to us that pretix is available with a comprehensive feature set under term that
are compatible with the `Open Source Definition`_. This enables event organizers from all industries and regions
to have access to a self-hosted, privacy-friendly and secure option to host their events.
However, developing and maintaining pretix is a lot of work. Between 2014 and 2021, we've received external
contributions from more than 150 individuals. Not counting translations over 90 % of the development was
done by staff engineers of rami.io GmbH, the company that started pretix. While we're very happy to receive many more
contributions in the future, we also want to ensure that we continue to be able to pay people working on pretix
full-time.
We believe our model creates a good balance between ensuring pretix is available freely as well as protecting our
business interests. Unlike licenses chosen by other projects recently, such as the Server-Side Public License, our
choice does not restrict using pretix for any possible use case, it just sets a few rules that you have to play by
if you do.
What do I need to do if I use pretix unmodified?
------------------------------------------------
If you use pretix without any modifications or plugins, you can use it for whatever you want, as long as you keep
all copyright notices (including the link to pretix at the bottom of the site) intact.
You are also allowed to make copies of the unmodified source code and distribute them to others as long as you keep
all copyright and license information intact.
If you install **plugins**, you must follow the same terms as when using a **modified** version (see below).
What do I need to do if I modify pretix?
----------------------------------------
If you want to modify pretix, you have the right to do so. However, you need to follow the following rules:
* If you **run it for your own events** (events run by you or your company as well as companies from the same
corporate groups) our additional permission allows you to do so **without needing to share your source code
modifications** as long as you keep the link to pretix at the bottom of the site intact.
* If you **run it for others**, for example as part of a Software-as-a-Service offering or a managed hosting service
you **must** make the source code **including all your modifications and all installed plugins** available under the
same license as pretix to every visitor of your site. You need to do so in a prominent place such as a link at the bottom of the
site. You also **must** keep the existing link intact.
You **may not** add additional restrictions on the result as a whole. You **may** add additional permissions, but
only on the parts you added. You **must** make clear which changes you made and you must not give the impression that
your modified version is an official version of pretix.
* If you **distribute** the modified version, for example as a source code or software package, you **must** license it
under the AGPL license with the same additional terms. You **may not** add additional restrictions on the result as a
whole. You **may** add additional permissions, but only on the parts you added. You **must** make clear which changes
you made and you must not give the impression that your modified version is an official version of pretix.
Does the AGPL copyleft mechanism extend to plugins?
---------------------------------------------------
Yes. pretix plugins are tightly integrated with pretix, so when running pretix together with a plugin in the same
environment they form a `combined work`_ and the copyleft mechanism of AGPL applies.
Can I create proprietary or secret plugins?
-------------------------------------------
Yes, you can create a proprietary or secret plugin, but it may only ever be **used** in an environment that is covered
by the additional permission from our license. As soon as the plugin is installed in an installation that is not covered
by our additional permission (e.g. when it is used in a SaaS environment) or covered by an active pretix Enterprise
license it **must** be released to the visitors of the site under the same license as pretix (like a modified version
of pretix).
What licenses can plugins use?
------------------------------
Technically, you can distribute a plugin under any free or proprietary license as long as it is distributed separately.
However, once it is either **distributed together with pretix or used in an environment not covered by our
additional permission** or an active pretix Enterprise license, you **must** release it to all recipients of the
distribution or all visitors of your site under the same license as pretix (like a modified version of pretix).
If you release a plugin publicly, it is therefore most practical to use a license that is `compatible to AGPL`_.
This includes most open source licenses such as AGPL, GPL, Apache, 3-clause BSD or MIT.
Note however that when you license a plugin with pure AGPL, it will be incompatible with our additional permission.
Therefore, if you want to use an AGPL-licensed plugin, you'll need to publish the source code of **all** your plugins
under AGPL terms **even if you only use it for your own events**. A plugin would add its `own additional permission`_
to its license to allow combining it with pretix for this use case.
To make things less complicated, if you want to distribute a plugin freely, we therefore recommend distributing the
plugin under **Apache License 2.0**, like we do for most plugins we distribute as open source.
What do I need to do if I want to contribute my changes back?
-------------------------------------------------------------
In order to retain the possibility for us to offer pretix in a dual licensing model, we unfortunately need you to sign
a Contributor License Agreement (CLA) that gives us permission to use your contribution in all present and future
distributions of pretix. We know the bureaucracy sucks. Sorry.
What if I want to re-use a minor part of pretix in my project?
--------------------------------------------------------------
This is the main part we dislike about AGPL: If you see a specific thing in pretix that you'd like to use in another
project, you'll need to distribute your other project under AGPL terms as well which is often not practical.
In this case, feel free to get in touch with us! We're happy to grant you special permission or pull the component
out into a separately, permissively licensed repository. We already did that with `django-hierarkey`_ and
`django-i18nfield`_ which have previously been parts of pretix.
What can I use the name "pretix" for?
-------------------------------------
The name pretix is a registered trademark by rami.io GmbH.
* You **may** use it to **indicate copyright**, such as in the "powered by pretix" or "based on pretix" line, or when
indicating that a distribution is based on pretix.
* You **may** use it to **indicate compatibility**, for example you are allowed to name your plugin "<name> for pretix"
or you may state that an external service is compatible with pretix.
* You **may not** give the impression that your modified version, plugin or compatible service is official or authorized
by rami.io GmbH or pretix unless we specifically allowed you to do so.
* You **may not** use it to name your modified version of pretix. End-users must be able to easily identify whether
a version of pretix is distributed by us.
* You **may not** use any variations of the name, such as "MyPretix".
.. _license file: https://github.com/pretix/pretix/blob/master/LICENSE
.. _GNU Affero General Public License 3: https://www.gnu.org/licenses/agpl-3.0.en.html
.. _compatible to AGPL: https://www.gnu.org/licenses/license-list.en.html#GPLCompatibleLicenses
.. _Open Source Definition: https://opensource.org/osd
.. _combined work: https://www.gnu.org/licenses/gpl-faq.html#GPLPlugins
.. _own additional permission: https://www.gnu.org/licenses/gpl-faq.html#GPLIncompatibleLibs
.. _django-hierarkey: https://github.com/raphaelm/django-hierarkey
.. _django-i18nfield: https://github.com/raphaelm/django-i18nfield

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

View File

@@ -82,9 +82,7 @@ namespaces
namespacing
natively
nginx
nodejs
NotificationType
npm
ons
optimizations
overpayment

View File

@@ -1,20 +0,0 @@
Use case: Early-bird tiers based on dates
-----------------------------------------
Let's say you run a conference that has the following pricing scheme:
* 12 to 6 months before the event: € 450
* 6 to 3 months before the event: € 550
* closer than 3 months to the event: € 650
Of course, you could just set up one product and change its price at the given dates manually, but if you want to set
this up automatically, here's how:
Create three products (e.g. "super early bird", "early bird", "regular ticket") with the respective prices and one shared
quota of your total event capacity. Then, set the **available from** and **available until** configuration fields of
the products to automatically turn them on and off based on the current date.
If you're in an event series, this will likely not help you since these dates would need to be the same for all dates
in your series. As an alternative, you can go to the "Dates" section of your event series, select one or more dates,
and scroll down to the "product settings" section. Here, you can also define availability times for individual products
just for this date individually.

View File

@@ -1,47 +0,0 @@
Use case: Early-bird tiers based on ticket numbers
--------------------------------------------------
Let's say you run a conference with 400 tickets that has the following pricing scheme:
* First 100 tickets ("super early bird"): € 450
* Next 100 tickets ("early bird"): € 550
* Remaining tickets ("regular"): € 650
First of all, create three products:
* "Super early bird ticket"
* "Early bird ticket"
* "Regular ticket"
Then, create three quotas:
* "Super early bird" with a **size of 100** and the "Super early bird ticket" product selected. At "Advanced options",
select the box "Close this quota permanently once it is sold out".
* "Early bird and lower" with a **size of 200** and both of the "Super early bird ticket" and "Early bird ticket"
products selected. At "Advanced options", select the box "Close this quota permanently once it is sold out".
* "All participants" with a **size of 400**, all three products selected and **no additional options**.
Next, modify the product "Regular ticket". In the section "Availability", you should look for the option "Only show
after sellout of" and select your quota "Early bird and lower". Do the same for the "Early bird ticket" with the quota
"Super early bird ticket".
This will ensure the following things:
* Each ticket level is only visible after the previous level is sold out.
* As soon as one level is really sold out, it's not coming back, because the quota "closes", i.e. locks in place.
* By creating a total quota of 400 with all tickets included, you can still make sure to sell the maximum number of
tickets, even if e.g. early-bird tickets are canceled.
Optionally, if you want to hide the early bird prices once they are sold out, go to "Settings", then "Display" and
select "Hide all products that are sold out". Of course, it might be a nice idea to keep showing the prices to remind
people to buy earlier next time ;)
Please note that there might be short time intervals where the prices switch back and forth: When the last early bird
tickets are in someone's cart (but not yet sold!), the early bird tickets will show as "Reserved" and the regular
tickets start showing up. However, if the customers holding the reservations do not complete their order,
the early bird tickets will become available again. This is not avoidable if we want to prevent malicious users
from blocking all the cheap tickets without an actual sale happening.

View File

@@ -1,20 +0,0 @@
Use case: Group discounts
-------------------------
Often times, you want to give discounts for whole groups attending your event. pretix can't automatically discount based on volume, but there's still some ways you can set up group tickets.
Flexible group sizes
""""""""""""""""""""
If you want to give out discounted tickets to groups starting at a given size, but still billed per person, you can do so by creating a special **Group ticket** at the per-person price and set the **Minimum amount per order** option of the ticket to the minimal group size.
For more complex use cases, you can also use add-on products that can be chosen multiple times.
This way, your ticket can be bought an arbitrary number of times but no less than the given minimal amount per order.
Fixed group sizes
"""""""""""""""""
If you want to sell group tickets in fixed sizes, e.g. a table of eight at your gala dinner, you can use product bundles. Assuming you already set up a ticket for admission of single persons, you then set up a second product **Table (8 persons)** with a discounted full price. Then, head to the **Bundled products** tab of that product and add one bundle configuration to include the single admission product **eight times**. Next, create an unlimited quota mapped to the new product.
This way, the purchase of a table will automatically create eight tickets, leading to a correct calculation of your total quota and, as expected, eight persons on your check-in list. You can even ask for the individual names of the persons during checkout.

View File

@@ -1,22 +0,0 @@
Use case: Mixed taxation
------------------------
Let's say you are a charitable organization in Germany and are allowed to charge a reduced tax rate of 7% for your educational event. However, your event includes a significant amount of food, you might need to charge a 19% tax rate on that portion. For example, your desired tax structure might then look like this:
* Conference ticket price: € 450 (incl. € 150 for food)
* incl. € 19.63 VAT at 7%
* incl. € 23.95 VAT at 19%
You can implement this in pretix using product bundles. In order to do so, you should create the following two products:
* Conference ticket at € 450 with a 7% tax rule
* Conference food at € 150 with a 19% tax rule and the option "**Only sell this product as part of a bundle**" set
In addition to your normal conference quota, you need to create an unlimited quota for the food product.
Then, head to the **Bundled products** tab of the "conference ticket" and add the "conference food" as a bundled product with a **designated price** of € 150.
Once a customer tries to buy the € 450 conference ticket, a sub-product will be added and the price will automatically be split into the two components, leading to a correct computation of taxes.
You can find more use cases in these specialized guides:

View File

@@ -1,78 +0,0 @@
Use case: Discounted packages
-----------------------------
Imagine you run a trade show that opens on three consecutive days and you want to have the following pricing:
* Single day: € 10
* Any two days: € 17
* All three days: € 25
In this case, there are multiple different ways you could set this up with pretix.
Option A: Combination products
""""""""""""""""""""""""""""""
With this option, you just set up all the different combinations someone could by as a separate product. In this case, you would need 7 products:
* Day 1 pass
* Day 2 pass
* Day 3 pass
* Day 1+2 pass
* Day 2+3 pass
* Day 1+3 pass
* All-day pass
Then, you create three quotas, each one with the maximum capacity of your venue on any given day:
* Day 1 quota, linked to "Day 1 pass", "Day 1+2 pass", "Day 1+3 pass", and "All-day pass"
* Day 2 quota, linked to "Day 2 pass", "Day 1+2 pass", "Day 2+3 pass", and "All-day pass"
* Day 3 quota, linked to "Day 3 pass", "Day 2+3 pass", "Day 1+3 pass", and "All-day pass"
This way, every person gets exactly one ticket that they can use for all days that they attend. You can later set up check-in lists appropriately to make sure only tickets valid for a certain day can be scanned on that day.
The benefit of this option is that your product structure and order structure stays very simple. However, the two-day packages scale badly when you need many products.
We recommend this setup for most setups in which the number of possible combinations does not exceed the number of parts (here: number of days) by much.
Option B: Add-ons and bundles
"""""""""""""""""""""""""""""
We can combine the two features "product add-ons" and "product bundles" to set this up in a different way. Here, you would create the following five products:
* Day 1 pass in a category called "Day passes"
* Day 2 pass in a category called "Day passes"
* Day 3 pass in a category called "Day passes"
* Two-day pass
* All-day pass
This time, you will need five quotas:
* Day 1 quota, linked to "Day 1 pass"
* Day 2 quota, linked to "Day 2 pass"
* Day 3 quota, linked to "Day 3 pass"
* Two-day pass quota, linked to "Two-day pass" (can be unlimited)
* All-day pass quota, linked to "All-day pass" (can be unlimited)
Then, you open the "Add-On" tab in the settings of the **Two-day pass** product and create a new add-on configuration specifying the following options:
* Category: "Day passes"
* Minimum number: 2
* Maximum number: 2
* Add-Ons are included in the price: Yes
This way, when buying a two-day pass, the user will be able to select *exactly* two days for free, which will then be added to the cart. Depending on your specific configuration, the user will now receive *two separate* tickets, one for each day.
For the all-day pass, you open the "Bundled products" tab in the settings of the **All-day pass** product and add **three** new bundled items with the following options:
* Bundled product: "Day 1/2/3"
* Bundled variation: None
* Count: 1
* Designated price: 0
This way, when buying an all-day pass, three free day passes will *automatically* be added to the cart. Depending on your specific configuration, the user will now receive *three separate* tickets, one for each day.
This approach makes your order data more complicated, since e.g. someone who buys an all-day pass now technically bought **four products**. However, this option allows for more flexibility when you have lots of options to choose from.
.. tip::
Depending on the packages you offer, you **might not need both the add-on and the bundle feature**, i.e. you only need the add-on feature for the two-day pass and only the bundle feature for the all-day pass. You could also set up the two-day pass like we showed here, but the all-day pass like in option A!

View File

@@ -1,13 +0,0 @@
Use case: Multiple price levels
-------------------------------
Imagine you're running a concert with general admission that sells a total of 200 tickets for two prices:
* Regular: € 25.00
* Students: € 19.00
You can either set up two different products called e.g. "Regular ticket" and "Student ticket" with the respective prices, or two variations within the same product. In this simple case, it really doesn't matter.
In addition, you will need quotas. If you do not care how many of your tickets are sold to students, you should set up just **one quota** of 200 called e.g. "General admission" that you link to **both products**.
If you want to limit the number of student tickets to 50 to ensure a certain minimum revenue, but do not want to limit the number of regular tickets artificially, we suggest you to create the same quota of 200 that is linked to both products, and then create a **second quota** of 50 that is only linked to the student ticket. This way, the system will reduce both quotas whenever a student ticket is sold and only the larger quota when a regular ticket is sold.

View File

@@ -1,28 +0,0 @@
Use case: Restricted audience
-----------------------------
Not all events are for everyone. Sometimes, there is a good reason to restrict access to your event or parts of your event only to a specific, invited group. There's two ways to implement this with pretix:
Option A: Required voucher codes
""""""""""""""""""""""""""""""""
If you check the option "**This product can only be bought using a voucher**" of one or multiple products, only people holding an applicable voucher code will be able to buy the product.
You can then generate voucher codes for the respective product and send them out to the group of possible attendees. If the recipients should still be able to choose between different products, you can create an additional quota and map the voucher to that quota instead of the products themselves.
There's also the second option "**This product will only be shown if a voucher matching the product is redeemed**". In this case, the existence of the product won't even be shown before a voucher code is entered useful for a VIP option in a shop where you also sell other products to the general public. Please note that this option does **not** work with vouchers assigned to a quota, only with vouchers assigned directly to the product.
This option is appropriate if you know the group of people beforehand, e.g. members of a club, and you can mail them their access codes.
Option B: Order approvals
"""""""""""""""""""""""""
If you do not know your audience already, but still want to restrict it to a certain group, e.g. people with a given profession, you can check the "**Buying this product requires approval**" in the settings of your product. If a customer tries to buy such a product, they will be able to place their order but can not proceed to payment. Instead, you will be asked to approve or deny the order and only if you approve it, we will send a payment link to the customer.
This requires the customer to interact with the ticket shop twice (once for the order, once for the payment) which adds a little more friction, but gives you full control over who attends the event.
Option C: Registered customers & memberships
""""""""""""""""""""""""""""""""""""""""""""
You can also do this by requiring that customers have a customer account and an active membership. You can find more
information on this mechanism in the :ref:`seasontickets` article.

View File

@@ -1,92 +0,0 @@
.. _seasontickets:
Use case: Season tickets
========================
Season tickets and similar time-based tickets are popular for swimming pools, sports clubs, theaters and lots of other
types of venues. In this article, we show you different ways to set them up with pretix. Of course, other types of
tickets such as week tickets, month tickets or tickets of ten can be created with the same mechanism.
There is a big difference between the two ways we show below.
With **Option A**, a customer who purchases a season ticket creates an account with their email address and a password
and the season ticket will be saved in that account. If the customer wants to use the season ticket, they need to buy
an additional free ticket for the specific event they want to visit. This makes sense for all events or venues with
**limited capacity** or **reserved seating**, because it still allows you to set an upper limit of people showing up
for a specific event or time slot.
With **Option B**, a customer who purchases a season ticket receives a single ticket with a single QR code that can be
used an unlimited number of times. This makes sense if the capacity of your venue is virtually unlimited and you do not
need to know in advance how many season ticket holders will show up.
Option A: Memberships and multiple tickets
""""""""""""""""""""""""""""""""""""""""""
Since this approach requires customers to be identified with a customer account, you first need to enable the customer
accounts feature in your organizer settings in the "Customer accounts" tab.
.. thumbnail:: ../../../screens/event/seasontickets_orgsettings.png
:align: center
:class: screenshot
After doing so, a new menu item "Customer accounts" will also show up in the main menu of your organizer account on
the left. Open it's menu and click on "Membership types". Then, select to "create a new membership type".
You can name the membership type in a way that clearly explains where it is valid, e.g. "season pass main location"
or "season pass all locations". There are a few details you can configure on this page, such as whether the season pass
can be used by multiple different persons, or if the season pass can be used for multiple tickets for the same time
slot. You can also define a maximum number of usages, which is useful if you e.g. use this feature to add a "ticket of
ten".
.. thumbnail:: ../../../screens/event/seasontickets_membershiptype.png
:align: center
:class: screenshot
Next, you need a way of selling these season passes. Theoretically this can be done through the same event series that
you usually use, but it's probably cleaner and easier to find for customers if you create a **new event** that you only
use to sell season passes. The start and end date of the new event should correspond to the dates of your season.
Inside the new event, you only need to create a single product which you can call "season ticket". Inside that product's
settings, head to the "Additional settings" section and look for the option "This product creates a membership of type".
Select the membership type you just created. By default, the checkbox "The duration of the membership is the same as the
duration of the event or event series date" is active, which is fine for our season ticket example, but you might need
to unset it and provide custom timing for other ticket types such as week passes.
.. thumbnail:: ../../../screens/event/seasontickets_issue.png
:align: center
:class: screenshot
To prevent confusion, it might be useful to turn off ticket downloading at "Settings" → "Tickets" for your new event.
That's it, you are now ready to sell season tickets!
We can now deal with how to use the season tickets. Move back to your existing event and create a new product
**or** product variation of your regular product which you call "ticket for season ticket holders" and assign a price
of zero. In the "Availability" section of the product or variation settings, check the option "Require a valid
membership" and again select the membership type you created. You can of course repeat this with all events the season
ticket holder should have access to.
.. thumbnail:: ../../../screens/event/seasontickets_require.png
:align: center
:class: screenshot
Option B: All-access in a single pass
"""""""""""""""""""""""""""""""""""""
If you have only a single event series with many time slots and you do not care how many season ticket holders show up,
there's a solution that does not require your customers to set up accounts and book a new ticket on every visit.
Instead, you can just create an additional product "Season ticket" that you enable either in a "special" date of your
event series just created for this purpose, or in all of your dates so it can be easily found by customers.
Then, you can set up your check-in lists with custom logic in the "Advanced" tab of your check-in list settings.
The logic needs to ensure the following requirements:
* Regular ticket holders can only get in during their assigned time frame **and** when they haven't used their ticket before.
* Season ticket holders can always get in.
Here's an example on how to set this up:
.. thumbnail:: ../../../screens/event/seasontickets_rules.png
:align: center
:class: screenshot

View File

@@ -1,24 +0,0 @@
Terminology
-----------
Products
A product is a basic entity that can be bought. You can think of it as a ticket type, but it can be more things than just a ticket, it can also be a piece of merchandise, a parking slot, etc.
You might find some places where they are called "items" instead, but we're trying to get rid of that.
Product categories
Products can be sorted into categories. Each product can only be in one category. Category are mostly used for grouping related products together to make your event page easier to read for buyers. However, we'll need categories as well to set up some of the structures outlined below.
Product variations
During creation of a product, you can decide that your product should have multiple variations. Variations of a product can differ in price, description, and availability, but are otherwise the same. You could use this e.g. for differentiating between a regular ticket and a discounted ticket for students, or when selling merchandise to differentiate the different sizes of a t-shirt.
Product add-ons
Add-ons are products that are sold together with another product (which we will call the base product in this case). For example, you could have a base product "Conference ticket" and then define multiple workshops that can be chosen as an add-on.
Product bundles
Bundles work very similarly to add-ons, but are different in the way that they are always automatically included with the base product and cannot be optional. In contrast to add-on products, the same product can be included multiple times in a bundle.
Quotas
Quotas define the availability of products. A quota has a size (i.e. the number of products in the inventory) and is mapped to one or multiple products or variations.
Questions
Questions are user-defined form fields that buyers will need to fill out when purchasing a product.

View File

@@ -1,17 +0,0 @@
Use case: Up-selling of ticket extras
-------------------------------------
Let's assume you're putting up a great music festival, and to save trouble with handling payments on-site, you want to sell parking spaces together with your ticket. By using our add-on feature, you can prompt all users to book the parking space (to make sure they see it) and ensure that only people with a ticket can book a parking space. You can set it up like this:
* Create a base product "Festival admission"
* Create a quota for the base product
* Create a category "Ticket extras" and check "Products in this category are add-on products"
* Create a product "Parking space" within that category
* Create a quota for the parking space product
* Go to the base product and select the tab "Add-Ons" at the top. Click "Add a new add-on" and choose the "Ticket extras" category. You can keep the numbers at 0 and 1.
During checkout, all buyers of the base product will now be prompted if they want to add the parking space.
.. tip::
You can also use add-on products for free things, just to keep tabs on capacity.

View File

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

View File

@@ -6,18 +6,353 @@ However, it is easy to get lost in the process or to get started with building y
Often times, there are multiple ways to do something that come with different advantages and disadvantages.
This guide will walk you through a number of typical examples of pretix event structures and will explain how to set them up feel free to just skip ahead to a section relevant for you.
Terminology
-----------
Products
A product is a basic entity that can be bought. You can think of it as a ticket type, but it can be more things than just a ticket, it can also be a piece of merchandise, a parking slot, etc.
You might find some places where they are called "items" instead, but we're trying to get rid of that.
Product categories
Products can be sorted into categories. Each product can only be in one category. Category are mostly used for grouping related products together to make your event page easier to read for buyers. However, we'll need categories as well to set up some of the structures outlined below.
Product variations
During creation of a product, you can decide that your product should have multiple variations. Variations of a product can differ in price, description, and availability, but are otherwise the same. You could use this e.g. for differentiating between a regular ticket and a discounted ticket for students, or when selling merchandise to differentiate the different sizes of a t-shirt.
Product add-ons
Add-ons are products that are sold together with another product (which we will call the base product in this case). For example, you could have a base product "Conference ticket" and then define multiple workshops that can be chosen as an add-on.
Product bundles
Bundles work very similarly to add-ons, but are different in the way that they are always automatically included with the base product and cannot be optional. In contrast to add-on products, the same product can be included multiple times in a bundle.
Quotas
Quotas define the availability of products. A quota has a size (i.e. the number of products in the inventory) and is mapped to one or multiple products or variations.
Questions
Questions are user-defined form fields that buyers will need to fill out when purchasing a product.
Use case: Multiple price levels
-------------------------------
Imagine you're running a concert with general admission that sells a total of 200 tickets for two prices:
* Regular: € 25.00
* Students: € 19.00
You can either set up two different products called e.g. "Regular ticket" and "Student ticket" with the respective prices, or two variations within the same product. In this simple case, it really doesn't matter.
In addition, you will need quotas. If you do not care how many of your tickets are sold to students, you should set up just **one quota** of 200 called e.g. "General admission" that you link to **both products**.
If you want to limit the number of student tickets to 50 to ensure a certain minimum revenue, but do not want to limit the number of regular tickets artificially, we suggest you to create the same quota of 200 that is linked to both products, and then create a **second quota** of 50 that is only linked to the student ticket. This way, the system will reduce both quotas whenever a student ticket is sold and only the larger quota when a regular ticket is sold.
Use case: Early-bird tiers based on dates
-----------------------------------------
Let's say you run a conference that has the following pricing scheme:
* 12 to 6 months before the event: € 450
* 6 to 3 months before the event: € 550
* closer than 3 months to the event: € 650
Of course, you could just set up one product and change its price at the given dates manually, but if you want to set this up automatically, here's how:
Create three products (e.g. "super early bird", "early bird", "regular ticket") with the respective prices and one shared quota of your total event capacity. Then, set the **available from** and **available until** configuration fields of the products to automatically turn them on and off based on the current date.
Use case: Early-bird tiers based on ticket numbers
--------------------------------------------------
Let's say you run a conference with 400 tickets that has the following pricing scheme:
* First 100 tickets ("super early bird"): € 450
* Next 100 tickets ("early bird"): € 550
* Remaining tickets ("regular"): € 650
First of all, create three products:
* "Super early bird ticket"
* "Early bird ticket"
* "Regular ticket"
Then, create three quotas:
* "Super early bird" with a **size of 100** and the "Super early bird ticket" product selected. At "Advanced options",
select the box "Close this quota permanently once it is sold out".
* "Early bird and lower" with a **size of 200** and both of the "Super early bird ticket" and "Early bird ticket"
products selected. At "Advanced options", select the box "Close this quota permanently once it is sold out".
* "All participants" with a **size of 400**, all three products selected and **no additional options**.
Next, modify the product "Regular ticket". In the section "Availability", you should look for the option "Only show
after sellout of" and select your quota "Early bird and lower". Do the same for the "Early bird ticket" with the quota
"Super early bird ticket".
This will ensure the following things:
* Each ticket level is only visible after the previous level is sold out.
* As soon as one level is really sold out, it's not coming back, because the quota "closes", i.e. locks in place.
* By creating a total quota of 400 with all tickets included, you can still make sure to sell the maximum number of
tickets, even if e.g. early-bird tickets are canceled.
Optionally, if you want to hide the early bird prices once they are sold out, go to "Settings", then "Display" and
select "Hide all products that are sold out". Of course, it might be a nice idea to keep showing the prices to remind
people to buy earlier next time ;)
Please note that there might be short time intervals where the prices switch back and forth: When the last early bird
tickets are in someone's cart (but not yet sold!), the early bird tickets will show as "Reserved" and the regular
tickets start showing up. However, if the customers holding the reservations do not complete their order,
the early bird tickets will become available again. This is not avoidable if we want to prevent malicious users
from blocking all the cheap tickets without an actual sale happening.
Use case: Up-selling of ticket extras
-------------------------------------
Let's assume you're putting up a great music festival, and to save trouble with handling payments on-site, you want to sell parking spaces together with your ticket. By using our add-on feature, you can prompt all users to book the parking space (to make sure they see it) and ensure that only people with a ticket can book a parking space. You can set it up like this:
* Create a base product "Festival admission"
* Create a quota for the base product
* Create a category "Ticket extras" and check "Products in this category are add-on products"
* Create a product "Parking space" within that category
* Create a quota for the parking space product
* Go to the base product and select the tab "Add-Ons" at the top. Click "Add a new add-on" and choose the "Ticket extras" category. You can keep the numbers at 0 and 1.
During checkout, all buyers of the base product will now be prompted if they want to add the parking space.
.. tip::
You can also use add-on products for free things, just to keep tabs on capacity.
Use case: Conference with workshops
-----------------------------------
When running a conference, you might also organize a number of workshops with smaller capacity. To be able to plan, it would be great to know which workshops an attendee plans to attend.
Option A: Questions
"""""""""""""""""""
Your first and simplest option is to just create a multiple-choice question. This has the upside of making it easy for users to change their mind later on, but will not allow you to restrict the number of attendees signing up for a given workshop or even charge extra for a given workshop.
Option B: Add-on products with fixed time slots
"""""""""""""""""""""""""""""""""""""""""""""""
The usually better option is to go with add-on products. Let's take for example the following conference schedule, in which the lecture can be attended by anyone, but the workshops only have space for 20 persons each:
==================== =================================== ===================================
Time Room A Room B
==================== =================================== ===================================
Wednesday morning Lecture
Wednesday afternoon Workshop A Workshop B
Thursday morning Workshop C Workshop D (20 € extra charge)
==================== =================================== ===================================
Assuming you already created one or more products for your general conference admission, we suggest that you additionally create:
* A category called "Workshops" with the checkbox "Products in this category are add-on products" activated
* A free product called "Wednesday afternoon" within the category "Workshops" and with two variations:
* Workshop A
* Workshop B
* A free product called "Thursday morning" within the category "Workshops" and with two variations:
* Workshop C
* Workshop D with a price of 20 €
* Four quotas for each of the workshops
* One add-on configuration on your base product that allows users to choose between 0 and 2 products from the category "Workshops"
Option C: Add-on products with variable time slots
""""""""""""""""""""""""""""""""""""""""""""""""""
The above option only works if your conference uses fixed time slots and every workshop uses exactly one time slot. If
your schedule looks like this, it's not going to work great:
+-------------+------------+-----------+
| Time | Room A | Room B |
+=============+============+===========+
| 09:00-11:00 | Talk 1 | Long |
+-------------+------------+ Workshop 1|
| 11:00-13:00 | Talk 2 | |
+-------------+------------+-----------+
| 14:00-16:00 | Long | Talk 3 |
+-------------+ workshop 2 +-----------+
| 16:00-18:00 | | Talk 4 |
+-------------+------------+-----------+
In this case, we recommend that you go to *Settings*, then *Plugins* and activate the plugin **Agenda constraints**.
Then, create a product (without variations) for every single part that should be bookable (talks 1-4 and long workshops
1 and 2) as well as appropriate quotas for each of them.
All of these products should be part of the same category. In your base product (e.g. your conference ticket), you
can then create an add-on product configuration allowing users to add products from this category.
If you edit these products, you will be able to enter the "Start date" and "End date" of the talk or workshop close
to the bottom of the page. If you fill in these values, pretix will automatically ensure no overlapping talks are
booked.
.. note::
This option is currently only available on pretix Hosted. If you are interested in using it with pretix Enterprise,
please contact sales@pretix.eu.
Use case: Discounted packages
-----------------------------
Imagine you run a trade show that opens on three consecutive days and you want to have the following pricing:
* Single day: € 10
* Any two days: € 17
* All three days: € 25
In this case, there are multiple different ways you could set this up with pretix.
Option A: Combination products
""""""""""""""""""""""""""""""
With this option, you just set up all the different combinations someone could by as a separate product. In this case, you would need 7 products:
* Day 1 pass
* Day 2 pass
* Day 3 pass
* Day 1+2 pass
* Day 2+3 pass
* Day 1+3 pass
* All-day pass
Then, you create three quotas, each one with the maximum capacity of your venue on any given day:
* Day 1 quota, linked to "Day 1 pass", "Day 1+2 pass", "Day 1+3 pass", and "All-day pass"
* Day 2 quota, linked to "Day 2 pass", "Day 1+2 pass", "Day 2+3 pass", and "All-day pass"
* Day 3 quota, linked to "Day 3 pass", "Day 2+3 pass", "Day 1+3 pass", and "All-day pass"
This way, every person gets exactly one ticket that they can use for all days that they attend. You can later set up check-in lists appropriately to make sure only tickets valid for a certain day can be scanned on that day.
The benefit of this option is that your product structure and order structure stays very simple. However, the two-day packages scale badly when you need many products.
We recommend this setup for most setups in which the number of possible combinations does not exceed the number of parts (here: number of days) by much.
Option B: Add-ons and bundles
"""""""""""""""""""""""""""""
We can combine the two features "product add-ons" and "product bundles" to set this up in a different way. Here, you would create the following five products:
* Day 1 pass in a category called "Day passes"
* Day 2 pass in a category called "Day passes"
* Day 3 pass in a category called "Day passes"
* Two-day pass
* All-day pass
This time, you will need five quotas:
* Day 1 quota, linked to "Day 1 pass"
* Day 2 quota, linked to "Day 2 pass"
* Day 3 quota, linked to "Day 3 pass"
* Two-day pass quota, linked to "Two-day pass" (can be unlimited)
* All-day pass quota, linked to "All-day pass" (can be unlimited)
Then, you open the "Add-On" tab in the settings of the **Two-day pass** product and create a new add-on configuration specifying the following options:
* Category: "Day passes"
* Minimum number: 2
* Maximum number: 2
* Add-Ons are included in the price: Yes
This way, when buying a two-day pass, the user will be able to select *exactly* two days for free, which will then be added to the cart. Depending on your specific configuration, the user will now receive *two separate* tickets, one for each day.
For the all-day pass, you open the "Bundled products" tab in the settings of the **All-day pass** product and add **three** new bundled items with the following options:
* Bundled product: "Day 1/2/3"
* Bundled variation: None
* Count: 1
* Designated price: 0
This way, when buying an all-day pass, three free day passes will *automatically* be added to the cart. Depending on your specific configuration, the user will now receive *three separate* tickets, one for each day.
This approach makes your order data more complicated, since e.g. someone who buys an all-day pass now technically bought **four products**. However, this option allows for more flexibility when you have lots of options to choose from.
.. tip::
Depending on the packages you offer, you **might not need both the add-on and the bundle feature**, i.e. you only need the add-on feature for the two-day pass and only the bundle feature for the all-day pass. You could also set up the two-day pass like we showed here, but the all-day pass like in option A!
Use case: Group discounts
-------------------------
Often times, you want to give discounts for whole groups attending your event. pretix can't automatically discount based on volume, but there's still some ways you can set up group tickets.
Flexible group sizes
""""""""""""""""""""
If you want to give out discounted tickets to groups starting at a given size, but still billed per person, you can do so by creating a special **Group ticket** at the per-person price and set the **Minimum amount per order** option of the ticket to the minimal group size.
For more complex use cases, you can also use add-on products that can be chosen multiple times.
This way, your ticket can be bought an arbitrary number of times but no less than the given minimal amount per order.
Fixed group sizes
"""""""""""""""""
If you want to sell group tickets in fixed sizes, e.g. a table of eight at your gala dinner, you can use product bundles. Assuming you already set up a ticket for admission of single persons, you then set up a second product **Table (8 persons)** with a discounted full price. Then, head to the **Bundled products** tab of that product and add one bundle configuration to include the single admission product **eight times**. Next, create an unlimited quota mapped to the new product.
This way, the purchase of a table will automatically create eight tickets, leading to a correct calculation of your total quota and, as expected, eight persons on your check-in list. You can even ask for the individual names of the persons during checkout.
Use case: Restricted audience
-----------------------------
Not all events are for everyone. Sometimes, there is a good reason to restrict access to your event or parts of your event only to a specific, invited group. There's two ways to implement this with pretix:
Option A: Required voucher codes
""""""""""""""""""""""""""""""""
If you check the option "**This product can only be bought using a voucher**" of one or multiple products, only people holding an applicable voucher code will be able to buy the product.
You can then generate voucher codes for the respective product and send them out to the group of possible attendees. If the recipients should still be able to choose between different products, you can create an additional quota and map the voucher to that quota instead of the products themselves.
There's also the second option "**This product will only be shown if a voucher matching the product is redeemed**". In this case, the existence of the product won't even be shown before a voucher code is entered useful for a VIP option in a shop where you also sell other products to the general public. Please note that this option does **not** work with vouchers assigned to a quota, only with vouchers assigned directly to the product.
This option is appropriate if you know the group of people beforehand, e.g. members of a club, and you can mail them their access codes.
Option B: Order approvals
"""""""""""""""""""""""""
If you do not know your audience already, but still want to restrict it to a certain group, e.g. people with a given profession, you can check the "**Buying this product requires approval**" in the settings of your product. If a customer tries to buy such a product, they will be able to place their order but can not proceed to payment. Instead, you will be asked to approve or deny the order and only if you approve it, we will send a payment link to the customer.
This requires the customer to interact with the ticket shop twice (once for the order, once for the payment) which adds a little more friction, but gives you full control over who attends the event.
Use case: Mixed taxation
------------------------
Let's say you are a charitable organization in Germany and are allowed to charge a reduced tax rate of 7% for your educational event. However, your event includes a significant amount of food, you might need to charge a 19% tax rate on that portion. For example, your desired tax structure might then look like this:
* Conference ticket price: € 450 (incl. € 150 for food)
* incl. € 19.63 VAT at 7%
* incl. € 23.95 VAT at 19%
You can implement this in pretix using product bundles. In order to do so, you should create the following two products:
* Conference ticket at € 450 with a 7% tax rule
* Conference food at € 150 with a 19% tax rule and the option "**Only sell this product as part of a bundle**" set
In addition to your normal conference quota, you need to create an unlimited quota for the food product.
Then, head to the **Bundled products** tab of the "conference ticket" and add the "conference food" as a bundled product with a **designated price** of € 150.
Once a customer tries to buy the € 450 conference ticket, a sub-product will be added and the price will automatically be split into the two components, leading to a correct computation of taxes.
You can find more use cases in these specialized guides:
More use cases
--------------
.. toctree::
:maxdepth: 1
guides/terminology
guides/pricelevels
guides/earlybird_dates
guides/earlybird_numbers
guides/upselling
guides/workshops
guides/packages
guides/groups
guides/restricted_audience
guides/timeslots
guides/season_tickets
guides/mixed_taxation

View File

@@ -86,7 +86,7 @@ going to develop around pretix, for example connect to pretix through our API, y
sold out. If a voucher is used to apply a discount, the price of the purchased product is reduced by the
discounted amount. Vouchers are connected to a specific event.
* - | |:gb:| **Gift card**
| |:de:| Wertgutschein
| |:de:| Geschenkgutschein
- A :ref:`gift card <giftcards>` is a coupon representing an exact amount of money that can be used for purchases
of any kind. Gift cards can be sold, created manually, or used as a method to refund your customer without paying
them back directly.
@@ -104,18 +104,13 @@ going to develop around pretix, for example connect to pretix through our API, y
* - | |:gb:| **Order code**
| |:de:| Bestellnummer
- An order code is the unique identifier of an order, usually consisting of 5 numbers and letters.
* - | |:gb:| **Customer**
| |:de:| Kund\*in
- A customer is the person who buys a ticket, regardless of who will be using it later. A customer can be defined
just by an email address or a name, or can have a persistent **customer account** they can log in to.
* - | |:gb:| **Order position**
| |:de:| Bestellposition
- An order position is a single line inside an order, representing the purchase of one specific product. If the
product is an admission product, this represents an attendee.
* - | |:gb:| **Attendee**
| |:de:| Teilnehmer\*in
- An attendee is the person designated to use a specific order position to access the event. It may be the same
or a different person as the customer.
* - | |:gb:| **Attendees**
| |:de:| Teilnehmende
- An attendee is the person designated to use a specific order position to access the event.
* - | |:gb:| **Fee**
| |:de:| Gebühr
- A fee is an additional type of line inside an order that represents a cost that needs to be paid by the customer,
@@ -126,14 +121,9 @@ going to develop around pretix, for example connect to pretix through our API, y
numbers and no longer change after they have been issued. Every invoice is connected to an order, but an order
can have multiple invoices: If an order changes, a cancellation document is created for the old invoice and a
new invoice is created.
* - | |:gb:| **Membership**
| |:de:| Mitgliedschaft
- A membership is a status attached customer, granting that customer a special right for a limited amount of time.
This special right could for example be the right to purchase a specific product. Memberships can be sold through
pretix as well.
* - | |:gb:| **Check-in**
| |:de:| Check-in
- A check-in is the event of someone's ticket being scanned at an entry or exit of the event.
- A check-in is the event of someone being successfully scanned at an entry or exit of the event.
* - | |:gb:| **Check-in list**
| |:de:| Check-in-Liste
- A check-in list is used to configure who can be scanned at a specific entry or exit of the event. Check-in lists
@@ -161,7 +151,7 @@ going to develop around pretix, for example connect to pretix through our API, y
- A badge refers to the file used as a name tag for an attendee of your event.
* - | |:gb:| **User**
| |:de:| Benutzer
- A user is anyone who can sign into the backend interface of pretix. Not to be confused with *Customer*.
- A user is anyone who can sign into the backend interface of pretix.
* - | |:gb:| **Team**
| |:de:| Team
- A :ref:`team <user-teams>` is a collection of users who are granted some level of access to a set of events.

View File

@@ -33,8 +33,6 @@ Permissions separate into two areas:
* Can create events To create a new event under this organizer account, users need to have this permission
* Can manage customer accounts This permission is required to view and change organizer-level customer accounts.
* Can change teams and permissions This permission is required to perform the kind of action you are doing right now.
Anyone with this permission can assign arbitrary other permissions to themselves, so this is the most powerful
permission there is to give.
@@ -61,8 +59,6 @@ Permissions separate into two areas:
* Can view vouchers This permission allows viewing the list of vouchers including the voucher codes themselves and
their redemption status.
* Can perform check-ins This permission allows using web-based features to perform ticket search and check-in.
* Can change vouchers This permission allows to create and modify vouchers in all their details. It only works
properly if the same users also have the "Can view vouchers" permission.

2
src/.gitignore vendored
View File

@@ -8,5 +8,3 @@ dist/
*.egg-info/
*.bak
pretix/static/jsi18n/
node_modules/

View File

@@ -26,5 +26,3 @@ recursive-include pretix/plugins/badges/templates *
recursive-include pretix/plugins/badges/static *
recursive-include pretix/plugins/returnurl/templates *
recursive-include pretix/plugins/returnurl/static *
recursive-include pretix/plugins/webcheckin/templates *
recursive-include pretix/plugins/webcheckin/static *

View File

@@ -12,7 +12,7 @@ localegen:
staticfiles: jsi18n
./manage.py collectstatic --noinput
compress: npminstall
compress:
./manage.py compress
jsi18n: localecompile
@@ -25,8 +25,6 @@ coverage:
coverage run -m py.test
npminstall:
# keep this in sync with setup.py!
mkdir -p pretix/static.dist/node_prefix/
cp -r pretix/static/npm_dir/* pretix/static.dist/node_prefix/
npm install --prefix=pretix/static.dist/node_prefix
mkdir -p pretix/static.dist/node_prefix
npm install --prefix=pretix/static.dist/node_prefix pretix/static/npm_dir/

View File

@@ -1,25 +1,4 @@
#!/usr/bin/env python3
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import os
import sys

View File

@@ -1,22 +1 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
__version__ = "4.0.0"
__version__ = "3.17.0.dev0"

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import os
import sys

View File

@@ -1,21 +1,12 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.apps import AppConfig
class PretixApiConfig(AppConfig):
name = 'pretix.api'
label = 'pretixapi'
def ready(self):
from . import signals, webhooks # noqa
default_app_config = 'pretix.api.PretixApiConfig'

View File

@@ -1,30 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.apps import AppConfig
class PretixApiConfig(AppConfig):
name = 'pretix.api'
label = 'pretixapi'
def ready(self):
from . import signals, webhooks # noqa

View File

@@ -1,21 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.contrib.auth.models import AnonymousUser
from django_scopes import scopes_disabled
from rest_framework import exceptions

View File

@@ -1,25 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.utils.translation import gettext_lazy as _
@@ -32,7 +10,7 @@ class FullAccessSecurityProfile:
class AllowListSecurityProfile:
allowlist = ()
allowlist = tuple()
def is_allowed(self, request):
key = (request.method, f"{request.resolver_match.namespace}:{request.resolver_match.url_name}")
@@ -117,23 +95,16 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
('GET', 'api-v1:taxrule-list'),
('GET', 'api-v1:ticketlayout-list'),
('GET', 'api-v1:ticketlayoutitem-list'),
('GET', 'api-v1:badgelayout-list'),
('GET', 'api-v1:badgeitem-list'),
('GET', 'api-v1:order-list'),
('POST', 'api-v1:order-list'),
('GET', 'api-v1:order-detail'),
('DELETE', 'api-v1:orderposition-detail'),
('PATCH', 'api-v1:orderposition-detail'),
('GET', 'api-v1:orderposition-answer'),
('GET', 'api-v1:orderposition-pdf_image'),
('POST', 'api-v1:order-mark-canceled'),
('POST', 'api-v1:order-mark_canceled'),
('POST', 'api-v1:orderpayment-list'),
('POST', 'api-v1:orderrefund-list'),
('POST', 'api-v1:orderrefund-done'),
('POST', 'api-v1:cartposition-list'),
('GET', 'api-v1:checkinlist-list'),
('POST', 'api-v1:checkinlistpos-redeem'),
('POST', 'plugins:pretix_posbackend:order.posprintlog'),
('DELETE', 'api-v1:cartposition-detail'),
('GET', 'api-v1:giftcard-list'),
('POST', 'api-v1:giftcard-transact'),
@@ -141,7 +112,6 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
('POST', 'plugins:pretix_posbackend:posreceipt-list'),
('POST', 'plugins:pretix_posbackend:posclosing-list'),
('POST', 'plugins:pretix_posbackend:posdebugdump-list'),
('GET', 'plugins:pretix_posbackend:poscashier-list'),
('POST', 'plugins:pretix_posbackend:stripeterminal.token'),
('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:event.settings'),

View File

@@ -1,37 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
# 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>.
#
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
#
# This file contains Apache-licensed contributions copyrighted by: Ture Gjørup
#
# 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.
from rest_framework.permissions import SAFE_METHODS, BasePermission
from pretix.api.models import OAuthAccessToken
@@ -80,12 +46,8 @@ class EventPermission(BasePermission):
else:
request.eventpermset = perm_holder.get_event_permission_set(request.organizer, request.event)
if isinstance(required_permission, (list, tuple)):
if not any(p in request.eventpermset for p in required_permission):
return False
else:
if required_permission and required_permission not in request.eventpermset:
return False
if required_permission and required_permission not in request.eventpermset:
return False
elif 'organizer' in request.resolver_match.kwargs:
if not request.organizer or not perm_holder.has_organizer_permission(request.organizer, request=request):
@@ -95,12 +57,8 @@ class EventPermission(BasePermission):
else:
request.orgapermset = perm_holder.get_organizer_permission_set(request.organizer)
if isinstance(required_permission, (list, tuple)):
if not any(p in request.eventpermset for p in required_permission):
return False
else:
if required_permission and required_permission not in request.orgapermset:
return False
if required_permission and required_permission not in request.orgapermset:
return False
if isinstance(request.auth, OAuthAccessToken):
if not request.auth.allow_scopes(['write']) and request.method not in SAFE_METHODS:

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.contrib.auth.models import AnonymousUser
from rest_framework import exceptions
from rest_framework.authentication import TokenAuthentication

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from rest_framework.response import Response
from rest_framework.views import exception_handler, status

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import json
from hashlib import sha1
@@ -72,7 +51,7 @@ class IdempotencyMiddleware:
if created:
resp = self.get_response(request)
with transaction.atomic():
if resp.status_code in (409, 429, 500, 503):
if resp.status_code in (409, 429, 503):
# This is the exception: These calls are *meant* to be retried!
call.delete()
else:
@@ -89,7 +68,7 @@ class IdempotencyMiddleware:
call.response_body = json.dumps(resp.data)
else:
call.response_body = repr(resp).encode()
call.response_headers = json.dumps(resp.headers._store)
call.response_headers = json.dumps(resp._headers)
call.locked = None
call.save(update_fields=['locked', 'response_code', 'response_headers',
'response_body'])

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from datetime import timedelta
from django.db import models

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from datetime import timedelta
from django.utils import timezone

View File

@@ -1,21 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#

View File

@@ -1,25 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import os
from datetime import timedelta
from django.core.files import File
@@ -126,10 +104,9 @@ class CartPositionCreateSerializer(I18nAwareModelSerializer):
if isinstance(answ_data['answer'], File):
an = answ_data.pop('answer')
answ = cp.answers.create(**answ_data, answer='')
answ.file.save(os.path.basename(an.name), an, save=False)
answ.file.save(an.name, an, save=False)
answ.answer = 'file://' + answ.file.name
answ.save()
an.close()
else:
answ = cp.answers.create(**answ_data)
answ.options.add(*options)

View File

@@ -1,29 +1,7 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.utils.translation import gettext as _
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from pretix.api.serializers.event import SubEventSerializer
from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.base.channels import get_all_sales_channels
from pretix.base.models import CheckinList
@@ -42,9 +20,6 @@ class CheckinListSerializer(I18nAwareModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if 'subevent' in self.context['request'].query_params.getlist('expand'):
self.fields['subevent'] = SubEventSerializer(read_only=True)
for exclude_field in self.context['request'].query_params.getlist('exclude'):
p = exclude_field.split('.')
if p[0] in self.fields:
@@ -75,6 +50,4 @@ class CheckinListSerializer(I18nAwareModelSerializer):
if channel not in get_all_sales_channels():
raise ValidationError(_('Unknown sales channel.'))
CheckinList.validate_rules(data.get('rules'))
return data

View File

@@ -1,37 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
# 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>.
#
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
#
# This file contains Apache-licensed contributions copyrighted by: Ture Gjørup
#
# 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
from django.conf import settings
@@ -264,7 +230,7 @@ class EventSerializer(I18nAwareModelSerializer):
def ignored_meta_properties(self):
perm_holder = (self.context['request'].auth if isinstance(self.context['request'].auth, (Device, TeamAPIToken))
else self.context['request'].user)
if perm_holder.has_organizer_permission(self.context['request'].organizer, 'can_change_organizer_settings', request=self.context['request']):
if perm_holder.has_organizer_permission('can_change_organizer_settings', request=self.context['request']):
return []
return [k for k, p in self.meta_properties.items() if p.protected]
@@ -343,7 +309,7 @@ class EventSerializer(I18nAwareModelSerializer):
# Item Meta properties
if item_meta_properties is not None:
current = list(event.item_meta_properties.all())
current = [imp for imp in event.item_meta_properties.all()]
for key, value in item_meta_properties.items():
prop = self.item_meta_props.get(key)
if prop in current:
@@ -400,7 +366,6 @@ class CloneEventSerializer(EventSerializer):
testmode = validated_data.pop('testmode', None)
has_subevents = validated_data.pop('has_subevents', None)
tz = validated_data.pop('timezone', None)
sales_channels = validated_data.pop('sales_channels', None)
new_event = super().create(validated_data)
event = Event.objects.filter(slug=self.context['event'], organizer=self.context['organizer'].pk).first()
@@ -412,8 +377,6 @@ class CloneEventSerializer(EventSerializer):
new_event.is_public = is_public
if testmode is not None:
new_event.testmode = testmode
if sales_channels is not None:
new_event.sales_channels = sales_channels
if has_subevents is not None:
new_event.has_subevents = has_subevents
new_event.save()
@@ -426,13 +389,13 @@ class CloneEventSerializer(EventSerializer):
class SubEventItemSerializer(I18nAwareModelSerializer):
class Meta:
model = SubEventItem
fields = ('item', 'price', 'disabled', 'available_from', 'available_until')
fields = ('item', 'price', 'disabled')
class SubEventItemVariationSerializer(I18nAwareModelSerializer):
class Meta:
model = SubEventItemVariation
fields = ('variation', 'price', 'disabled', 'available_from', 'available_until')
fields = ('variation', 'price', 'disabled')
class SubEventSerializer(I18nAwareModelSerializer):
@@ -507,7 +470,7 @@ class SubEventSerializer(I18nAwareModelSerializer):
def ignored_meta_properties(self):
perm_holder = (self.context['request'].auth if isinstance(self.context['request'].auth, (Device, TeamAPIToken))
else self.context['request'].user)
if perm_holder.has_organizer_permission(self.context['request'].organizer, 'can_change_organizer_settings', request=self.context['request']):
if perm_holder.has_organizer_permission('can_change_organizer_settings', request=self.context['request']):
return []
return [k for k, p in self.meta_properties.items() if p.protected]
@@ -665,7 +628,6 @@ class EventSettingsSerializer(SettingsSerializer):
'redirect_to_checkout_directly',
'frontpage_subevent_ordering',
'event_list_type',
'event_list_available_only',
'frontpage_text',
'event_info_text',
'attendee_names_asked',
@@ -697,7 +659,6 @@ class EventSettingsSerializer(SettingsSerializer):
'ticket_download_nonadm',
'ticket_download_pending',
'ticket_download_require_validated_email',
'ticket_secret_length',
'mail_prefix',
'mail_from',
'mail_from_name',
@@ -723,7 +684,6 @@ class EventSettingsSerializer(SettingsSerializer):
'invoice_include_expire_date',
'invoice_address_explanation_text',
'invoice_email_attachment',
'invoice_email_organizer',
'invoice_address_from_name',
'invoice_address_from',
'invoice_address_from_zipcode',

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django import forms
from django.http import QueryDict
from rest_framework import serializers
@@ -39,18 +18,18 @@ class FormFieldWrapperField(serializers.Field):
simple_mappings = (
(forms.DateField, serializers.DateField, ()),
(forms.TimeField, serializers.TimeField, ()),
(forms.SplitDateTimeField, serializers.DateTimeField, ()),
(forms.DateTimeField, serializers.DateTimeField, ()),
(forms.DateField, serializers.DateField, tuple()),
(forms.TimeField, serializers.TimeField, tuple()),
(forms.SplitDateTimeField, serializers.DateTimeField, tuple()),
(forms.DateTimeField, serializers.DateTimeField, tuple()),
(forms.DecimalField, serializers.DecimalField, ('max_digits', 'decimal_places', 'min_value', 'max_value')),
(forms.FloatField, serializers.FloatField, ()),
(forms.IntegerField, serializers.IntegerField, ()),
(forms.EmailField, serializers.EmailField, ()),
(forms.UUIDField, serializers.UUIDField, ()),
(forms.URLField, serializers.URLField, ()),
(forms.NullBooleanField, serializers.NullBooleanField, ()),
(forms.BooleanField, serializers.BooleanField, ()),
(forms.FloatField, serializers.FloatField, tuple()),
(forms.IntegerField, serializers.IntegerField, tuple()),
(forms.EmailField, serializers.EmailField, tuple()),
(forms.UUIDField, serializers.UUIDField, tuple()),
(forms.URLField, serializers.URLField, tuple()),
(forms.NullBooleanField, serializers.NullBooleanField, tuple()),
(forms.BooleanField, serializers.BooleanField, tuple()),
)

View File

@@ -1,37 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
# 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>.
#
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
#
# This file contains Apache-licensed contributions copyrighted by: pajowu
#
# 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.
from collections import OrderedDict
from django.core.exceptions import ValidationError

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.conf import settings
from i18nfield.fields import I18nCharField, I18nTextField
from i18nfield.strings import LazyI18nString

View File

@@ -1,43 +1,8 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
# 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>.
#
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
#
# This file contains Apache-licensed contributions copyrighted by: Patrick Arminio, Ture Gjørup, pajowu
#
# 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.
from decimal import Decimal
from django.core.exceptions import ValidationError
from django.db import transaction
from django.db.models import QuerySet
from django.utils.functional import cached_property, lazy
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
@@ -57,12 +22,7 @@ class InlineItemVariationSerializer(I18nAwareModelSerializer):
class Meta:
model = ItemVariation
fields = ('id', 'value', 'active', 'description',
'position', 'default_price', 'price', 'original_price',
'require_membership', 'require_membership_types',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['require_membership_types'].queryset = lazy(lambda: self.context['event'].organizer.membership_types.all(), QuerySet)
'position', 'default_price', 'price', 'original_price')
class ItemVariationSerializer(I18nAwareModelSerializer):
@@ -72,12 +32,7 @@ class ItemVariationSerializer(I18nAwareModelSerializer):
class Meta:
model = ItemVariation
fields = ('id', 'value', 'active', 'description',
'position', 'default_price', 'price', 'original_price',
'require_membership', 'require_membership_types',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['require_membership_types'].queryset = self.context['event'].organizer.membership_types.all()
'position', 'default_price', 'price', 'original_price')
class InlineItemBundleSerializer(serializers.ModelSerializer):
@@ -171,17 +126,9 @@ class ItemSerializer(I18nAwareModelSerializer):
'require_voucher', 'hide_without_voucher', 'allow_cancel', 'require_bundling',
'min_per_order', 'max_per_order', 'checkin_attention', 'has_variations', 'variations',
'addons', 'bundles', 'original_price', 'require_approval', 'generate_tickets',
'show_quota_left', 'hidden_if_available', 'allow_waitinglist', 'issue_giftcard', 'meta_data',
'require_membership', 'require_membership_types', 'grant_membership_type',
'grant_membership_duration_like_event', 'grant_membership_duration_days',
'grant_membership_duration_months')
'show_quota_left', 'hidden_if_available', 'allow_waitinglist', 'issue_giftcard', 'meta_data')
read_only_fields = ('has_variations',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['require_membership_types'].queryset = self.context['event'].organizer.membership_types.all()
self.fields['grant_membership_type'].queryset = self.context['event'].organizer.membership_types.all()
def validate(self, data):
data = super().validate(data)
if self.instance and ('addons' in data or 'variations' in data or 'bundles' in data):
@@ -248,10 +195,7 @@ class ItemSerializer(I18nAwareModelSerializer):
item = Item.objects.create(**validated_data)
for variation_data in variations_data:
require_membership_types = variation_data.pop('require_membership_types')
v = ItemVariation.objects.create(item=item, **variation_data)
if require_membership_types:
v.require_membership_types.add(*require_membership_types)
ItemVariation.objects.create(item=item, **variation_data)
for addon_data in addons_data:
ItemAddOn.objects.create(base_item=item, **addon_data)
for bundle_data in bundles_data:
@@ -338,8 +282,8 @@ class QuestionSerializer(I18nAwareModelSerializer):
fields = ('id', 'question', 'type', 'required', 'items', 'options', 'position',
'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_file_portrait')
'valid_number_max', 'valid_date_min', 'valid_date_max', 'valid_datetime_min', 'valid_datetime_max'
)
def validate_identifier(self, value):
Question._clean_identifier(self.context['event'], value, self.instance)

View File

@@ -1,27 +1,4 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import json
import logging
import os
from collections import Counter, defaultdict
from decimal import Decimal
@@ -36,16 +13,12 @@ from rest_framework.exceptions import ValidationError
from rest_framework.relations import SlugRelatedField
from rest_framework.reverse import reverse
from pretix.api.serializers.event import SubEventSerializer
from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.api.serializers.item import (
InlineItemVariationSerializer, ItemSerializer,
)
from pretix.base.channels import get_all_sales_channels
from pretix.base.decimal import round_decimal
from pretix.base.i18n import language
from pretix.base.models import (
CachedFile, Checkin, Customer, Invoice, InvoiceAddress, InvoiceLine, Item,
CachedFile, Checkin, Invoice, InvoiceAddress, InvoiceLine, Item,
ItemVariation, Order, OrderPosition, Question, QuestionAnswer, Seat,
SubEvent, TaxRule, Voucher,
)
@@ -60,8 +33,6 @@ from pretix.base.settings import COUNTRIES_WITH_STATE_IN_ADDRESS
from pretix.base.signals import register_ticket_outputs
from pretix.multidomain.urlreverse import build_absolute_uri
logger = logging.getLogger(__name__)
class CompatibleCountryField(serializers.Field):
def to_internal_value(self, data):
@@ -172,8 +143,6 @@ class AnswerSerializer(I18nAwareModelSerializer):
return q
def _handle_file_upload(self, data):
if data['answer'] == 'file:keep':
return data
try:
ao = self.context["request"].user or self.context["request"].auth
cf = CachedFile.objects.get(
@@ -319,11 +288,7 @@ class PdfDataSerializer(serializers.Field):
self.context['vars_images'] = get_images(self.context['request'].event)
for k, f in self.context['vars'].items():
try:
res[k] = f['evaluate'](instance, instance.order, ev)
except:
logger.exception('Evaluating PDF variable failed')
res[k] = '(error)'
res[k] = f['evaluate'](instance, instance.order, ev)
if not hasattr(ev, '_cached_meta_data'):
ev._cached_meta_data = ev.meta_data
@@ -340,19 +305,10 @@ class PdfDataSerializer(serializers.Field):
for k, f in self.context['vars_images'].items():
if 'etag' in f:
try:
has_image = etag = f['etag'](instance, instance.order, ev)
except:
has_image = False
etag = None
logger.exception('Evaluating PDF variable failed')
has_image = etag = f['etag'](instance, instance.order, ev)
else:
try:
has_image = f['valuate'](instance, instance.order, ev)
etag = None
except:
has_image = False
logger.exception('Evaluating PDF variable failed')
has_image = f['etag'](instance, instance.order, ev)
etag = None
if has_image:
url = reverse('api-v1:orderposition-pdf_image', kwargs={
'organizer': instance.order.event.organizer.slug,
@@ -393,9 +349,8 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
request = self.context.get('request')
if request and (not request.query_params.get('pdf_data', 'false') == 'true' or 'can_view_orders' not in request.eventpermset):
self.fields.pop('pdf_data', None)
if 'request' in self.context and not self.context['request'].query_params.get('pdf_data', 'false') == 'true':
self.fields.pop('pdf_data')
def validate(self, data):
if data.get('attendee_name') and data.get('attendee_name_parts'):
@@ -458,8 +413,6 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
if isinstance(answ_data['answer'], File):
a.file.save(answ_data['answer'].name, answ_data['answer'], save=False)
a.answer = 'file://' + a.file.name
elif a.answer.startswith('file://') and answ_data['answer'] == "file:keep":
pass # keep current file
else:
for attr, value in answ_data.items():
setattr(a, attr, value)
@@ -468,7 +421,7 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
if isinstance(answ_data['answer'], File):
an = answ_data.pop('answer')
a = instance.answers.create(**answ_data, answer='')
a.file.save(os.path.basename(an.name), an, save=False)
a.file.save(an.name, an, save=False)
a.answer = 'file://' + a.file.name
a.save()
else:
@@ -531,18 +484,6 @@ class CheckinListOrderPositionSerializer(OrderPositionSerializer):
'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'require_attention',
'order__status')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if 'subevent' in self.context['request'].query_params.getlist('expand'):
self.fields['subevent'] = SubEventSerializer(read_only=True)
if 'item' in self.context['request'].query_params.getlist('expand'):
self.fields['item'] = ItemSerializer(read_only=True, context=self.context)
if 'variation' in self.context['request'].query_params.getlist('expand'):
self.fields['variation'] = InlineItemVariationSerializer(read_only=True)
class OrderPaymentTypeField(serializers.Field):
# TODO: Remove after pretix 2.2
@@ -625,7 +566,6 @@ class OrderSerializer(I18nAwareModelSerializer):
payment_date = OrderPaymentDateField(source='*', read_only=True)
payment_provider = OrderPaymentTypeField(source='*', read_only=True)
url = OrderURLField(source='*', read_only=True)
customer = serializers.SlugRelatedField(slug_field='identifier', read_only=True)
class Meta:
model = Order
@@ -633,18 +573,18 @@ class OrderSerializer(I18nAwareModelSerializer):
'code', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
'payment_provider', 'fees', 'total', 'comment', 'invoice_address', 'positions', 'downloads',
'checkin_attention', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
'url', 'customer'
'url'
)
read_only_fields = (
'code', 'status', 'testmode', 'secret', 'datetime', 'expires', 'payment_date',
'payment_provider', 'fees', 'total', 'positions', 'downloads', 'customer',
'payment_provider', 'fees', 'total', 'positions', 'downloads',
'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel'
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.context['request'].query_params.get('pdf_data', 'false') == 'true':
self.fields['positions'].child.fields.pop('pdf_data', None)
self.fields['positions'].child.fields.pop('pdf_data')
for exclude_field in self.context['request'].query_params.getlist('exclude'):
p = exclude_field.split('.')
@@ -909,18 +849,16 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
payment_date = serializers.DateTimeField(required=False, allow_null=True)
send_email = serializers.BooleanField(default=False, required=False)
simulate = serializers.BooleanField(default=False, required=False)
customer = serializers.SlugRelatedField(slug_field='identifier', queryset=Customer.objects.none(), required=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['positions'].child.fields['voucher'].queryset = self.context['event'].vouchers.all()
self.fields['customer'].queryset = self.context['event'].organizer.customers.all()
class Meta:
model = Order
fields = ('code', 'status', 'testmode', 'email', 'phone', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
'invoice_address', 'positions', 'checkin_attention', 'payment_info', 'payment_date', 'consume_carts',
'force', 'send_email', 'simulate', 'customer')
'force', 'send_email', 'simulate')
def validate_payment_provider(self, pp):
if pp is None:
@@ -1275,7 +1213,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
if isinstance(answ_data['answer'], File):
an = answ_data.pop('answer')
answ = pos.answers.create(**answ_data, answer='')
answ.file.save(os.path.basename(an.name), an, save=False)
answ.file.save(an.name, an, save=False)
answ.answer = 'file://' + answ.file.name
answ.save()
else:

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import logging
from decimal import Decimal
@@ -34,9 +13,8 @@ from pretix.api.serializers.settings import SettingsSerializer
from pretix.base.auth import get_auth_backends
from pretix.base.i18n import get_language_without_region
from pretix.base.models import (
Customer, Device, GiftCard, GiftCardTransaction, Membership,
MembershipType, Organizer, SeatingPlan, Team, TeamAPIToken, TeamInvite,
User,
Device, GiftCard, GiftCardTransaction, Organizer, SeatingPlan, Team,
TeamAPIToken, TeamInvite, User,
)
from pretix.base.models.seating import SeatingPlanLayoutValidator
from pretix.base.services.mail import SendMailException, mail
@@ -62,44 +40,6 @@ class SeatingPlanSerializer(I18nAwareModelSerializer):
fields = ('id', 'name', 'layout')
class CustomerSerializer(I18nAwareModelSerializer):
identifier = serializers.CharField(read_only=True)
name = serializers.CharField(read_only=True)
last_login = serializers.DateTimeField(read_only=True)
date_joined = serializers.DateTimeField(read_only=True)
last_modified = serializers.DateTimeField(read_only=True)
class Meta:
model = Customer
fields = ('identifier', 'email', 'name', 'name_parts', 'is_active', 'is_verified', 'last_login', 'date_joined',
'locale', 'last_modified')
class MembershipTypeSerializer(I18nAwareModelSerializer):
class Meta:
model = MembershipType
fields = ('id', 'name', 'transferable', 'allow_parallel_usage', 'max_usages')
class MembershipSerializer(I18nAwareModelSerializer):
customer = serializers.SlugRelatedField(slug_field='identifier', queryset=Customer.objects.none())
class Meta:
model = Membership
fields = ('id', 'testmode', 'customer', 'membership_type', 'date_start', 'date_end', 'attendee_name_parts')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['customer'].queryset = self.context['organizer'].customers.all()
self.fields['membership_type'].queryset = self.context['organizer'].membership_types.all()
def update(self, instance, validated_data):
validated_data['customer'] = instance.customer # no modifying
validated_data['testmode'] = instance.testmode # no modifying
return super().update(instance, validated_data)
class GiftCardSerializer(I18nAwareModelSerializer):
value = serializers.DecimalField(max_digits=10, decimal_places=2, min_value=Decimal('0.00'))
@@ -155,7 +95,7 @@ class TeamSerializer(serializers.ModelSerializer):
'id', 'name', 'all_events', 'limit_events', 'can_create_events', 'can_change_teams',
'can_change_organizer_settings', 'can_manage_gift_cards', 'can_change_event_settings',
'can_change_items', 'can_view_orders', 'can_change_orders', 'can_view_vouchers',
'can_change_vouchers', 'can_checkin_orders', 'can_manage_customers'
'can_change_vouchers'
)
def validate(self, data):
@@ -273,8 +213,6 @@ class TeamMemberSerializer(serializers.ModelSerializer):
class OrganizerSettingsSerializer(SettingsSerializer):
default_fields = [
'customer_accounts',
'customer_accounts_link_by_email',
'contact_mail',
'imprint_url',
'organizer_info_text',

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import logging
from django.core.files import File

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from rest_framework.exceptions import ValidationError
from pretix.api.serializers.i18n import I18nAwareModelSerializer

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.core.exceptions import ValidationError
from rest_framework import serializers

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from datetime import timedelta
from django.dispatch import Signal, receiver

View File

@@ -1,37 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
# 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>.
#
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
#
# This file contains Apache-licensed contributions copyrighted by: Ture Gjørup
#
# 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 importlib
from django.apps import apps
@@ -54,9 +20,6 @@ orga_router.register(r'subevents', event.SubEventViewSet)
orga_router.register(r'webhooks', webhooks.WebHookViewSet)
orga_router.register(r'seatingplans', organizer.SeatingPlanViewSet)
orga_router.register(r'giftcards', organizer.GiftCardViewSet)
orga_router.register(r'customers', organizer.CustomerViewSet)
orga_router.register(r'memberships', organizer.MembershipViewSet)
orga_router.register(r'membershiptypes', organizer.MembershipTypeViewSet)
orga_router.register(r'teams', organizer.TeamViewSet)
orga_router.register(r'devices', organizer.DeviceViewSet)
orga_router.register(r'exporters', exporters.OrganizerExportersViewSet, basename='exporters')
@@ -118,12 +81,14 @@ urlpatterns = [
name="event.settings"),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/', include(event_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/teams/(?P<team>[^/]+)/', include(team_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/items/(?P<item>[^/]+)/', include(item_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/items/(?P<item>[^/]+)/',
include(item_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/questions/(?P<question>[^/]+)/',
include(question_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/checkinlists/(?P<list>[^/]+)/',
include(checkinlist_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/orders/(?P<order>[^/]+)/', include(order_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/orders/(?P<order>[^/]+)/',
include(order_router.urls)),
re_path(r"^oauth/authorize$", oauth.AuthorizationView.as_view(), name="authorize"),
re_path(r"^oauth/token$", oauth.TokenView.as_view(), name="token"),
re_path(r"^oauth/revoke_token$", oauth.RevokeTokenView.as_view(), name="revoke-token"),

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from calendar import timegm
from django.db.models import Max

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.db import transaction
from rest_framework import status, viewsets
from rest_framework.filters import OrderingFilter

View File

@@ -1,28 +1,7 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import django_filters
from django.core.exceptions import ValidationError
from django.db.models import (
Count, Exists, F, Max, OrderBy, OuterRef, Prefetch, Q, Subquery,
Count, Exists, F, Max, OuterRef, Prefetch, Q, Subquery,
)
from django.db.models.functions import Coalesce
from django.http import Http404
@@ -46,13 +25,13 @@ from pretix.base.models import (
CachedFile, Checkin, CheckinList, Event, Order, OrderPosition, Question,
)
from pretix.base.services.checkin import (
CheckInError, RequiredQuestionsError, SQLLogic, perform_checkin,
CheckInError, RequiredQuestionsError, perform_checkin,
)
from pretix.helpers.database import FixedOrderBy
with scopes_disabled():
class CheckinListFilter(FilterSet):
subevent_match = django_filters.NumberFilter(method='subevent_match_qs')
ends_after = django_filters.rest_framework.IsoDateTimeFilter(method='ends_after_qs')
class Meta:
model = CheckinList
@@ -63,35 +42,19 @@ with scopes_disabled():
Q(subevent_id=value) | Q(subevent_id__isnull=True)
)
def ends_after_qs(self, queryset, name, value):
expr = (
Q(subevent__isnull=True) |
Q(
Q(Q(subevent__date_to__isnull=True) & Q(subevent__date_from__gte=value))
| Q(Q(subevent__date_to__isnull=False) & Q(subevent__date_to__gte=value))
)
)
return queryset.filter(expr)
class CheckinListViewSet(viewsets.ModelViewSet):
serializer_class = CheckinListSerializer
queryset = CheckinList.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_class = CheckinListFilter
permission = ('can_view_orders', 'can_checkin_orders',)
permission = 'can_view_orders'
write_permission = 'can_change_event_settings'
def get_queryset(self):
qs = self.request.event.checkin_lists.prefetch_related(
'limit_products',
)
if 'subevent' in self.request.query_params.getlist('expand'):
qs = qs.prefetch_related(
'subevent', 'subevent__event', 'subevent__subeventitem_set', 'subevent__subeventitemvariation_set',
'subevent__seat_category_mappings', 'subevent__meta_values'
)
return qs
def perform_create(self, serializer):
@@ -192,37 +155,15 @@ class CheckinListViewSet(viewsets.ModelViewSet):
with scopes_disabled():
class CheckinOrderPositionFilter(OrderPositionFilter):
check_rules = django_filters.rest_framework.BooleanFilter(method='check_rules_qs')
# check_rules is currently undocumented on purpose, let's get a feel for the performance impact first
def __init__(self, *args, **kwargs):
self.checkinlist = kwargs.pop('checkinlist')
super().__init__(*args, **kwargs)
def has_checkin_qs(self, queryset, name, value):
return queryset.filter(last_checked_in__isnull=not value)
def check_rules_qs(self, queryset, name, value):
if not self.checkinlist.rules:
return queryset
return queryset.filter(SQLLogic(self.checkinlist).apply(self.checkinlist.rules))
class ExtendedBackend(DjangoFilterBackend):
def get_filterset_kwargs(self, request, queryset, view):
kwargs = super().get_filterset_kwargs(request, queryset, view)
# merge filterset kwargs provided by view class
if hasattr(view, 'get_filterset_kwargs'):
kwargs.update(view.get_filterset_kwargs())
return kwargs
class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = CheckinListOrderPositionSerializer
queryset = OrderPosition.all.none()
filter_backends = (ExtendedBackend, RichOrderingFilter)
filter_backends = (DjangoFilterBackend, RichOrderingFilter)
ordering = ('attendee_name_cached', 'positionid')
ordering_fields = (
'order__code', 'order__datetime', 'positionid', 'attendee_name',
@@ -238,26 +179,16 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
'display_name': Coalesce('attendee_name_cached', 'addon_to__attendee_name_cached')
},
'last_checked_in': {
'_order': OrderBy(F('last_checked_in'), nulls_first=True),
'_order': FixedOrderBy(F('last_checked_in'), nulls_first=True),
},
'-last_checked_in': {
'_order': OrderBy(F('last_checked_in'), nulls_last=True, descending=True),
'_order': FixedOrderBy(F('last_checked_in'), nulls_last=True, descending=True),
},
}
filterset_class = CheckinOrderPositionFilter
permission = ('can_view_orders', 'can_checkin_orders')
write_permission = ('can_change_orders', 'can_checkin_orders')
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['event'] = self.request.event
return ctx
def get_filterset_kwargs(self):
return {
'checkinlist': self.checkinlist,
}
permission = 'can_view_orders'
write_permission = 'can_change_orders'
@cached_property
def checkinlist(self):
@@ -278,7 +209,7 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
order__event=self.request.event,
).annotate(
last_checked_in=Subquery(cqs)
).prefetch_related('order__event', 'order__event__organizer')
)
if self.checkinlist.subevent:
qs = qs.filter(
subevent=self.checkinlist.subevent
@@ -324,22 +255,6 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
if not self.checkinlist.all_products and not ignore_products:
qs = qs.filter(item__in=self.checkinlist.limit_products.values_list('id', flat=True))
if 'subevent' in self.request.query_params.getlist('expand'):
qs = qs.prefetch_related(
'subevent', 'subevent__event', 'subevent__subeventitem_set', 'subevent__subeventitemvariation_set',
'subevent__seat_category_mappings', 'subevent__meta_values'
)
if 'item' in self.request.query_params.getlist('expand'):
qs = qs.prefetch_related('item', 'item__addons', 'item__bundles', 'item__meta_values', 'item__variations').select_related('item__tax_rule')
if 'variation' in self.request.query_params.getlist('expand'):
qs = qs.prefetch_related('variation')
if 'pk' not in self.request.resolver_match.kwargs and 'can_view_orders' not in self.request.eventpermset \
and len(self.request.query_params.get('search', '')) < 3:
qs = qs.none()
return qs
@action(detail=False, methods=['POST'], url_name='redeem', url_path='(?P<pk>.*)/redeem')
@@ -394,54 +309,52 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
except ValidationError:
pass
with language(self.request.event.settings.locale):
try:
perform_checkin(
op=op,
clist=self.checkinlist,
given_answers=given_answers,
force=force,
ignore_unpaid=ignore_unpaid,
nonce=nonce,
datetime=dt,
questions_supported=self.request.data.get('questions_supported', True),
canceled_supported=self.request.data.get('canceled_supported', False),
user=self.request.user,
auth=self.request.auth,
type=type,
)
except RequiredQuestionsError as e:
return Response({
'status': 'incomplete',
'require_attention': op.item.checkin_attention or op.order.checkin_attention,
'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data,
'questions': [
QuestionSerializer(q).data for q in e.questions
]
}, status=400)
except CheckInError as e:
op.order.log_action('pretix.event.checkin.denied', data={
'position': op.id,
'positionid': op.positionid,
'errorcode': e.code,
'force': force,
'datetime': dt,
'type': type,
'list': self.checkinlist.pk
}, user=self.request.user, auth=self.request.auth)
return Response({
'status': 'error',
'reason': e.code,
'reason_explanation': e.reason,
'require_attention': op.item.checkin_attention or op.order.checkin_attention,
'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data
}, status=400)
else:
return Response({
'status': 'ok',
'require_attention': op.item.checkin_attention or op.order.checkin_attention,
'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data
}, status=201)
try:
perform_checkin(
op=op,
clist=self.checkinlist,
given_answers=given_answers,
force=force,
ignore_unpaid=ignore_unpaid,
nonce=nonce,
datetime=dt,
questions_supported=self.request.data.get('questions_supported', True),
canceled_supported=self.request.data.get('canceled_supported', False),
user=self.request.user,
auth=self.request.auth,
type=type,
)
except RequiredQuestionsError as e:
return Response({
'status': 'incomplete',
'require_attention': op.item.checkin_attention or op.order.checkin_attention,
'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data,
'questions': [
QuestionSerializer(q).data for q in e.questions
]
}, status=400)
except CheckInError as e:
op.order.log_action('pretix.event.checkin.denied', data={
'position': op.id,
'positionid': op.positionid,
'errorcode': e.code,
'force': force,
'datetime': dt,
'type': type,
'list': self.checkinlist.pk
}, user=self.request.user, auth=self.request.auth)
return Response({
'status': 'error',
'reason': e.code,
'require_attention': op.item.checkin_attention or op.order.checkin_attention,
'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data
}, status=400)
else:
return Response({
'status': 'ok',
'require_attention': op.item.checkin_attention or op.order.checkin_attention,
'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data
}, status=201)
def _handle_file_upload(self, data):
try:

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import logging
from django.db.models import Exists, OuterRef, Q
@@ -74,8 +53,8 @@ class DeviceSerializer(serializers.ModelSerializer):
class InitializeView(APIView):
authentication_classes = ()
permission_classes = ()
authentication_classes = tuple()
permission_classes = tuple()
def post(self, request, format=None):
serializer = InitializationRequestSerializer(data=request.data)
@@ -187,16 +166,6 @@ class EventSelectionView(APIView):
qs = qs.annotate(has_cl=Exists(has_cl)).filter(has_cl=True)
return qs
def _max_first_date_event(self, a, b):
if a and b:
return a if a.first_date > b.first_date else b
return a or b
def _min_first_date_event(self, a, b):
if a and b:
return a if a.first_date < b.first_date else b
return a or b
def get(self, request, format=None):
device = request.auth
current_event = None
@@ -210,35 +179,27 @@ class EventSelectionView(APIView):
if current_event:
current_ev = current_subevent or current_event
else:
current_ev = None
# The event that is selected might not currently be running. We cannot rely on all events having a proper end date.
# Also, if events run back-to-back, the later event can overlap the earlier event due to its admission-time.
# In any case, we'll need to decide between the current event, the event that last started (and might still be running) and the
# event that starts next (and might already be letting people in), so let's get these as well!
# No matter if current event is given in query_params, always check whether another event already
# started overlaps can happen through e.g. admission-time overlapping or misconfig).
# Note that last_started here means either admission started or the event itself started.
last_started_ev = self._max_first_date_event(
self.base_event_qs.filter(first_date__lte=now()).last(),
self.base_subevent_qs.filter(first_date__lte=now()).last()
)
if last_started_ev and current_ev != last_started_ev and \
last_started_ev.date_to and now() < last_started_ev.date_to:
return self._suggest_event(current_event, last_started_ev)
if current_event:
current_ev_start = current_ev.date_admission or current_ev.date_from
tz = current_event.timezone
if current_ev.date_to and current_ev_start <= now() < current_ev.date_to:
if current_ev.date_to and current_ev_start < now() < current_ev.date_to:
# The event that is selected is currently running. Good enough.
return Response(status=status.HTTP_304_NOT_MODIFIED)
upcoming_ev = self._min_first_date_event(
self.base_event_qs.filter(first_date__gt=now()).first(),
self.base_subevent_qs.filter(first_date__gt=now()).first()
)
# The event that is selected is not currently running. We cannot rely on all events having a proper end date.
# In any case, we'll need to decide between the event that last started (and might still be running) and the
# event that starts next (and might already be letting people in), so let's get these two!
last_started_ev = self.base_event_qs.filter(first_date__lte=now()).last() or self.base_subevent_qs.filter(
first_date__lte=now()).last()
upcoming_event = self.base_event_qs.filter(first_date__gt=now()).first()
upcoming_subevent = self.base_subevent_qs.filter(first_date__gt=now()).first()
if upcoming_event and upcoming_subevent:
if upcoming_event.first_date > upcoming_subevent.first_date:
upcoming_ev = upcoming_subevent
else:
upcoming_ev = upcoming_event
else:
upcoming_ev = upcoming_event or upcoming_subevent
if not upcoming_ev and not last_started_ev:
# Ooops, no events here
@@ -250,8 +211,12 @@ class EventSelectionView(APIView):
# No event upcoming, so let's take the next one
return self._suggest_event(current_event, last_started_ev)
if last_started_ev.date_to and now() < last_started_ev.date_to:
# The event that last started is currently running. Good enough.
return self._suggest_event(current_event, last_started_ev)
if not current_event:
tz = (upcoming_ev or last_started_ev).timezone
tz = (upcoming_event or last_started_ev).timezone
lse_d = last_started_ev.date_from.astimezone(tz).date()
upc_d = upcoming_ev.date_from.astimezone(tz).date()

View File

@@ -1,37 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
# 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>.
#
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
#
# This file contains Apache-licensed contributions copyrighted by: Ture Gjørup
#
# 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 django_filters
from django.db import transaction
from django.db.models import ProtectedError, Q
@@ -39,7 +5,7 @@ from django.utils.timezone import now
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
from rest_framework import filters, serializers, views, viewsets
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.exceptions import PermissionDenied
from rest_framework.response import Response
from pretix.api.auth.permission import EventCRUDPermission
@@ -120,13 +86,6 @@ class EventViewSet(viewsets.ModelViewSet):
ordering_fields = ('date_from', 'slug')
filterset_class = EventFilter
def get_copy_from_queryset(self):
if isinstance(self.request.auth, (TeamAPIToken, Device)):
return self.request.auth.get_events_with_any_permission()
elif self.request.user.is_authenticated:
return self.request.user.get_events_with_any_permission(self.request)
return Event.objects.none()
def get_queryset(self):
if isinstance(self.request.auth, (TeamAPIToken, Device)):
qs = self.request.auth.get_events_with_any_permission()
@@ -180,45 +139,8 @@ class EventViewSet(viewsets.ModelViewSet):
)
def perform_create(self, serializer):
copy_from = None
if 'clone_from' in self.request.GET:
src = self.request.GET.get('clone_from')
try:
if '/' in src:
copy_from = self.get_copy_from_queryset().get(
organizer__slug=src.split('/')[0],
slug=src.split('/')[1]
)
else:
copy_from = self.get_copy_from_queryset().get(
organizer=self.request.organizer,
slug=src
)
except Event.DoesNotExist:
raise ValidationError('Event to copy from was not found')
print(copy_from, self.request.GET)
new_event = serializer.save(organizer=self.request.organizer)
if copy_from:
new_event.copy_data_from(copy_from)
if 'plugins' in serializer.validated_data:
new_event.set_active_plugins(serializer.validated_data['plugins'])
if 'is_public' in serializer.validated_data:
new_event.is_public = serializer.validated_data['is_public']
if 'testmode' in serializer.validated_data:
new_event.testmode = serializer.validated_data['testmode']
if 'sales_channels' in serializer.validated_data:
new_event.sales_channels = serializer.validated_data['sales_channels']
if 'has_subevents' in serializer.validated_data:
new_event.has_subevents = serializer.validated_data['has_subevents']
new_event.save()
if 'timezone' in serializer.validated_data:
new_event.settings.timezone = serializer.validated_data['timezone']
else:
serializer.instance.set_defaults()
serializer.save(organizer=self.request.organizer)
serializer.instance.set_defaults()
serializer.instance.log_action(
'pretix.event.added',
user=self.request.user,
@@ -337,7 +259,7 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet):
qs = filter_qs_by_attr(qs, self.request)
return qs.prefetch_related(
'subeventitem_set', 'subeventitemvariation_set', 'seat_category_mappings', 'meta_values'
'subeventitem_set', 'subeventitemvariation_set', 'seat_category_mappings'
)
def list(self, request, **kwargs):

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from datetime import timedelta
from celery.result import AsyncResult

View File

@@ -1,37 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
# 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>.
#
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
#
# This file contains Apache-licensed contributions copyrighted by: Ture Gjørup
#
# 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 django_filters
from django.db.models import Q
from django.shortcuts import get_object_or_404
@@ -83,9 +49,7 @@ class ItemViewSet(ConditionalListView, viewsets.ModelViewSet):
write_permission = 'can_change_items'
def get_queryset(self):
return self.request.event.items.select_related('tax_rule').prefetch_related(
'variations', 'addons', 'bundles', 'meta_values'
).all()
return self.request.event.items.select_related('tax_rule').prefetch_related('variations', 'addons', 'bundles').all()
def perform_create(self, serializer):
serializer.save(event=self.request.event)
@@ -152,7 +116,6 @@ class ItemVariationViewSet(viewsets.ModelViewSet):
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['item'] = self.item
ctx['event'] = self.request.event
return ctx
def perform_create(self, serializer):

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import logging
from django import forms

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import datetime
import mimetypes
import os
@@ -27,7 +6,7 @@ from decimal import Decimal
import django_filters
import pytz
from django.db import transaction
from django.db.models import Exists, F, OuterRef, Prefetch, Q, Subquery
from django.db.models import Exists, F, OuterRef, Prefetch, Q
from django.db.models.functions import Coalesce, Concat
from django.http import FileResponse, HttpResponse
from django.shortcuts import get_object_or_404
@@ -97,31 +76,30 @@ with scopes_disabled():
model = Order
fields = ['code', 'status', 'email', 'locale', 'testmode', 'require_approval']
@scopes_disabled()
def subevent_after_qs(self, qs, name, value):
qs = qs.filter(
pk__in=Subquery(
qs = qs.annotate(
has_se_after=Exists(
OrderPosition.all.filter(
subevent_id__in=SubEvent.objects.filter(
Q(date_to__gt=value) | Q(date_from__gt=value, date_to__isnull=True),
event=self.request.event
Q(date_to__gt=value) | Q(date_from__gt=value, date_to__isnull=True), event=OuterRef(OuterRef('event_id'))
).values_list('id'),
).values_list('order_id')
order_id=OuterRef('pk'),
)
)
)
).filter(has_se_after=True)
return qs
def subevent_before_qs(self, qs, name, value):
qs = qs.filter(
pk__in=Subquery(
qs = qs.annotate(
has_se_before=Exists(
OrderPosition.all.filter(
subevent_id__in=SubEvent.objects.filter(
Q(date_from__lt=value),
event=self.request.event
Q(date_from__lt=value), event=OuterRef(OuterRef('event_id'))
).values_list('id'),
).values_list('order_id')
order_id=OuterRef('pk'),
)
)
)
).filter(has_se_before=True)
return qs
def search_qs(self, qs, name, value):
@@ -609,22 +587,9 @@ class OrderViewSet(viewsets.ModelViewSet):
)
with language(order.locale, self.request.event.settings.region):
payment = order.payments.last()
order_placed.send(self.request.event, order=order)
if order.status == Order.STATUS_PAID:
order_paid.send(self.request.event, order=order)
order.log_action(
'pretix.event.order.paid',
{
'provider': payment.provider if payment else None,
'info': {},
'date': now().isoformat(),
'force': False
},
user=request.user if request.user.is_authenticated else None,
auth=request.auth,
)
gen_invoice = invoice_qualified(order) and (
(order.event.settings.get('invoice_generate') == 'True') or
@@ -635,6 +600,7 @@ class OrderViewSet(viewsets.ModelViewSet):
invoice = generate_invoice(order, trigger_pdf=True)
if send_mail:
payment = order.payments.last()
free_flow = (
payment and order.total == Decimal('0.00') and order.status == Order.STATUS_PAID and
not order.require_approval and payment.provider == "free"

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from decimal import Decimal
import django_filters
@@ -38,16 +17,14 @@ from rest_framework.viewsets import GenericViewSet
from pretix.api.models import OAuthAccessToken
from pretix.api.serializers.organizer import (
CustomerSerializer, DeviceSerializer, GiftCardSerializer,
GiftCardTransactionSerializer, MembershipSerializer,
MembershipTypeSerializer, OrganizerSerializer, OrganizerSettingsSerializer,
SeatingPlanSerializer, TeamAPITokenSerializer, TeamInviteSerializer,
TeamMemberSerializer, TeamSerializer,
DeviceSerializer, GiftCardSerializer, GiftCardTransactionSerializer,
OrganizerSerializer, OrganizerSettingsSerializer, SeatingPlanSerializer,
TeamAPITokenSerializer, TeamInviteSerializer, TeamMemberSerializer,
TeamSerializer,
)
from pretix.base.models import (
Customer, Device, GiftCard, GiftCardTransaction, Membership,
MembershipType, Organizer, SeatingPlan, Team, TeamAPIToken, TeamInvite,
User,
Device, GiftCard, GiftCardTransaction, Organizer, SeatingPlan, Team,
TeamAPIToken, TeamInvite, User,
)
from pretix.base.settings import SETTINGS_AFFECTING_CSS
from pretix.helpers.dicts import merge_dicts
@@ -482,163 +459,3 @@ class OrganizerSettingsView(views.APIView):
'request': request
})
return Response(s.data)
with scopes_disabled():
class CustomerFilter(FilterSet):
email = django_filters.CharFilter(field_name='email', lookup_expr='iexact')
class Meta:
model = Customer
fields = ['email']
class CustomerViewSet(viewsets.ModelViewSet):
serializer_class = CustomerSerializer
queryset = Customer.objects.none()
permission = 'can_manage_customers'
lookup_field = 'identifier'
filter_backends = (DjangoFilterBackend,)
filterset_class = CustomerFilter
def get_queryset(self):
qs = self.request.organizer.customers.all()
return qs
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['organizer'] = self.request.organizer
return ctx
def perform_destroy(self, instance):
raise MethodNotAllowed("Customers cannot be deleted.")
@transaction.atomic()
def perform_create(self, serializer):
inst = serializer.save(organizer=self.request.organizer)
serializer.instance.log_action(
'pretix.customer.created',
user=self.request.user,
auth=self.request.auth,
data=self.request.data,
)
return inst
@transaction.atomic()
def perform_update(self, serializer):
inst = serializer.save(organizer=self.request.organizer)
serializer.instance.log_action(
'pretix.customer.changed',
user=self.request.user,
auth=self.request.auth,
data=self.request.data,
)
return inst
@action(detail=True, methods=["POST"])
@transaction.atomic()
def anonymize(self, request, **kwargs):
o = self.get_object()
o.anonymize()
o.log_action('pretix.customer.anonymized', user=self.request.user, auth=self.request.auth)
return Response(CustomerSerializer(o).data, status=status.HTTP_200_OK)
class MembershipTypeViewSet(viewsets.ModelViewSet):
serializer_class = MembershipTypeSerializer
queryset = MembershipType.objects.none()
permission = 'can_change_organizer_settings'
def get_queryset(self):
qs = self.request.organizer.membership_types.all()
return qs
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['organizer'] = self.request.organizer
return ctx
def perform_destroy(self, instance):
if not instance.allow_delete():
raise PermissionDenied("Can only be deleted if unused.")
instance.log_action(
'pretix.membershiptype.deleted',
user=self.request.user,
auth=self.request.auth,
data={'id': instance.pk}
)
instance.delete()
@transaction.atomic()
def perform_create(self, serializer):
inst = serializer.save(organizer=self.request.organizer)
serializer.instance.log_action(
'pretix.membershiptype.created',
user=self.request.user,
auth=self.request.auth,
data=self.request.data,
)
return inst
@transaction.atomic()
def perform_update(self, serializer):
inst = serializer.save(organizer=self.request.organizer)
serializer.instance.log_action(
'pretix.membershiptype.changed',
user=self.request.user,
auth=self.request.auth,
data=self.request.data,
)
return inst
with scopes_disabled():
class MembershipFilter(FilterSet):
customer = django_filters.CharFilter(field_name='customer__identifier', lookup_expr='iexact')
class Meta:
model = Membership
fields = ['customer', 'membership_type', 'testmode']
class MembershipViewSet(viewsets.ModelViewSet):
serializer_class = MembershipSerializer
queryset = Membership.objects.none()
permission = 'can_manage_customers'
filter_backends = (DjangoFilterBackend,)
filterset_class = MembershipFilter
def get_queryset(self):
return Membership.objects.filter(
customer__organizer=self.request.organizer
)
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['organizer'] = self.request.organizer
return ctx
def perform_destroy(self, instance):
raise MethodNotAllowed("Memberships cannot be deleted. You can change the date instead.")
@transaction.atomic()
def perform_create(self, serializer):
inst = serializer.save()
serializer.instance.customer.log_action(
'pretix.customer.membership.created',
user=self.request.user,
auth=self.request.auth,
data=self.request.data,
)
return inst
@transaction.atomic()
def perform_update(self, serializer):
inst = serializer.save()
serializer.instance.customer.log_action(
'pretix.customer.membership.changed',
user=self.request.user,
auth=self.request.auth,
data=self.request.data,
)
return inst

View File

@@ -1,24 +1,3 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import datetime
from django.utils.timezone import now

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