Compare commits

..

1 Commits

Author SHA1 Message Date
Raphael Michel
8113d9471d Add tests for locking in API 2023-09-26 01:13:04 +02:00
920 changed files with 361366 additions and 715882 deletions

View File

@@ -10,9 +10,7 @@ updates:
schedule:
interval: "daily"
versioning-strategy: increase
open-pull-requests-limit: 10
- package-ecosystem: "npm"
directory: "/src/pretix/static/npm_dir"
schedule:
interval: "monthly"
open-pull-requests-limit: 5

View File

@@ -26,19 +26,19 @@ jobs:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v4
- uses: actions/cache@v1
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install system dependencies
run: sudo apt update && sudo apt install -y gettext unzip
run: sudo apt update && sudo apt install gettext unzip
- name: Install Python dependencies
run: pip3 install -U setuptools build pip check-manifest
- name: Run check-manifest

View File

@@ -25,19 +25,19 @@ jobs:
name: Spellcheck
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Set up Python 3.11
uses: actions/setup-python@v5
uses: actions/setup-python@v1
with:
python-version: 3.11
- uses: actions/cache@v4
- uses: actions/cache@v1
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install system packages
run: sudo apt update && sudo apt install -y enchant-2 hunspell aspell-en
run: sudo apt update && sudo apt install enchant-2 hunspell aspell-en
- name: Install Dependencies
run: pip3 install -Ur requirements.txt
working-directory: ./doc

View File

@@ -23,21 +23,21 @@ jobs:
runs-on: ubuntu-22.04
name: Check gettext syntax
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Set up Python 3.11
uses: actions/setup-python@v5
uses: actions/setup-python@v1
with:
python-version: 3.11
- uses: actions/cache@v4
- uses: actions/cache@v1
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install system packages
run: sudo apt update && sudo apt -y install gettext
run: sudo apt update && sudo apt install gettext
- name: Install Dependencies
run: pip3 install uv && uv pip install --system -e ".[dev]"
run: pip3 install -e ".[dev]"
- name: Compile messages
run: python manage.py compilemessages
working-directory: ./src
@@ -48,12 +48,12 @@ jobs:
runs-on: ubuntu-22.04
name: Spellcheck
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Set up Python 3.11
uses: actions/setup-python@v5
uses: actions/setup-python@v1
with:
python-version: 3.11
- uses: actions/cache@v4
- uses: actions/cache@v1
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
@@ -62,7 +62,7 @@ jobs:
- name: Install system packages
run: sudo apt update && sudo apt install enchant-2 hunspell hunspell-de-de aspell-en aspell-de
- name: Install Dependencies
run: pip3 install uv && uv pip install --system -e ".[dev]"
run: pip3 install -e ".[dev]"
- name: Spellcheck translations
run: potypo
working-directory: ./src

View File

@@ -23,19 +23,19 @@ jobs:
name: isort
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Set up Python 3.11
uses: actions/setup-python@v5
uses: actions/setup-python@v1
with:
python-version: 3.11
- uses: actions/cache@v4
- uses: actions/cache@v1
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install Dependencies
run: pip3 install uv && uv pip install --system -e ".[dev]" psycopg2-binary
run: pip3 install -e ".[dev]" psycopg2-binary
- name: Run isort
run: isort -c .
working-directory: ./src
@@ -43,19 +43,19 @@ jobs:
name: flake8
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Set up Python 3.11
uses: actions/setup-python@v5
uses: actions/setup-python@v1
with:
python-version: 3.11
- uses: actions/cache@v4
- uses: actions/cache@v1
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install Dependencies
run: pip3 install uv && uv pip install --system -e ".[dev]" psycopg2-binary
run: pip3 install -e ".[dev]" psycopg2-binary
- name: Run flake8
run: flake8 .
working-directory: ./src
@@ -63,9 +63,9 @@ jobs:
name: licenseheaders
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Set up Python 3.11
uses: actions/setup-python@v5
uses: actions/setup-python@v1
with:
python-version: 3.11
- name: Install Dependencies

View File

@@ -5,6 +5,7 @@ on:
branches: [ master ]
paths-ignore:
- 'doc/**'
- 'src/pretix/locale/**'
pull_request:
branches: [ master ]
paths-ignore:
@@ -30,35 +31,29 @@ jobs:
python-version: "3.9"
- database: sqlite
python-version: "3.10"
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: pretix
options: >-
--health-cmd "pg_isready -U postgres -d pretix"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- uses: harmon758/postgresql-action@v1
with:
postgresql version: '15'
postgresql db: 'pretix'
postgresql user: 'postgres'
postgresql password: 'postgres'
if: matrix.database == 'postgres'
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v4
- uses: actions/cache@v1
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install system dependencies
run: sudo apt update && sudo apt install -y gettext
run: sudo apt update && sudo apt install gettext
- name: Install Python dependencies
run: pip3 install uv && uv pip install --system -e ".[dev]" psycopg2-binary
run: pip3 install --ignore-requires-python -e ".[dev]" psycopg2-binary # We ignore that flake8 needs newer python as we don't run flake8 during tests
- name: Run checks
run: python manage.py check
working-directory: ./src
@@ -70,15 +65,15 @@ jobs:
run: make all compress
- name: Run tests
working-directory: ./src
run: PRETIX_CONFIG_FILE=tests/ci_${{ matrix.database }}.cfg py.test -n 3 -p no:sugar --cov=./ --cov-report=xml tests --maxfail=100
run: PRETIX_CONFIG_FILE=tests/travis_${{ matrix.database }}.cfg py.test -n 3 -p no:sugar --cov=./ --cov-report=xml --reruns 3 tests --maxfail=100
- name: Run concurrency tests
working-directory: ./src
run: PRETIX_CONFIG_FILE=tests/ci_${{ matrix.database }}.cfg py.test tests/concurrency_tests/ --reuse-db
run: PRETIX_CONFIG_FILE=tests/travis_${{ matrix.database }}.cfg py.test tests/concurrency_tests/ --reruns 0 --reuse-db
if: matrix.database == 'postgres'
- name: Upload coverage
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v1
with:
file: src/coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
fail_ci_if_error: true
if: matrix.database == 'postgres' && matrix.python-version == '3.11'

View File

@@ -1,30 +1,29 @@
before_script:
tests:
image:
name: pretix/ci-image
stage: test
before_script:
- pip install -U pip uv
- uv pip install --system -U wheel setuptools
script:
- uv pip install --system -e ".[dev]"
- virtualenv env
- source env/bin/activate
- pip install -U pip wheel setuptools
- XDG_CACHE_HOME=/cache pip3 install -e ".[dev]"
- cd src
- python manage.py check
- make all compress
- PRETIX_CONFIG_FILE=tests/ci_sqlite.cfg py.test -n 3 tests --maxfail=100
- py.test --reruns 3 -n 3 tests
tags:
- python3
except:
- pypi
pypi:
stage: release
image:
name: pretix/ci-image
before_script:
- cat $PYPIRC > ~/.pypirc
- pip install -U pip uv
- uv pip install --system -U wheel setuptools twine build pretix-plugin-build check-manifest
script:
- uv pip install --system -e ".[dev]"
- cp /keys/.pypirc ~/.pypirc
- virtualenv env
- source env/bin/activate
- pip install -U pip wheel setuptools check-manifest twine
- XDG_CACHE_HOME=/cache pip3 install -e ".[dev]"
- python setup.py sdist
- uv pip install --system dist/pretix-*.tar.gz
- pip install dist/pretix-*.tar.gz
- python -m pretix migrate
- python -m pretix check
- cd src
@@ -34,12 +33,13 @@ pypi:
- python -m build
- twine check dist/*
- twine upload dist/*
tags:
- python3
only:
- pypi
artifacts:
paths:
- src/dist/
stages:
- test
- build

View File

@@ -1 +0,0 @@
17

View File

@@ -10,8 +10,6 @@ recursive-include src/pretix/helpers/locale *
recursive-include src/pretix/base/templates *
recursive-include src/pretix/control/templates *
recursive-include src/pretix/presale/templates *
recursive-include src/pretix/plugins/autocheckin/templates *
recursive-include src/pretix/plugins/autocheckin/static *
recursive-include src/pretix/plugins/banktransfer/templates *
recursive-include src/pretix/plugins/banktransfer/static *
recursive-include src/pretix/plugins/manualpayment/templates *

View File

@@ -60,14 +60,6 @@ http {
deny all;
return 404;
}
location /static/staticfiles.json {
deny all;
return 404;
}
location /static/CACHE/manifest.json {
deny all;
return 404;
}
location /static/ {
alias /pretix/src/pretix/static.dist/;
access_log off;

View File

@@ -5,7 +5,7 @@ export DATA_DIR=/data/
export HOME=/pretix
AUTOMIGRATE=${AUTOMIGRATE:-yes}
NUM_WORKERS_DEFAULT=$((2 * $(nproc)))
NUM_WORKERS_DEFAULT=$((2 * $(nproc --all)))
export NUM_WORKERS=${NUM_WORKERS:-$NUM_WORKERS_DEFAULT}
if [ ! -d /data/logs ]; then
@@ -47,7 +47,7 @@ if [ "$1" == "taskworker" ]; then
fi
if [ "$1" == "upgrade" ]; then
exec python3 -m pretix updateassets
exec python3 -m pretix updatestyles
fi
exec python3 -m pretix "$@"

View File

@@ -42,6 +42,7 @@ Example::
currency=EUR
datadir=/data
plugins_default=pretix.plugins.sendmail,pretix.plugins.statistics
cookie_domain=.pretix.de
``instance_name``
The name of this installation. Default: ``pretix.de``
@@ -52,18 +53,10 @@ Example::
``currency``
The default currency as a three-letter code. Defaults to ``EUR``.
``cachedir``
The local path to a directory where temporary files will be stored.
Defaults to the ``cache`` directory below the ``datadir``.
``datadir``
The local path to a data directory that will be used for storing user uploads and similar
data. Defaults to the value of the environment variable ``DATA_DIR`` or ``data``.
``logdir``
The local path to a directory where log files will be stored.
Defaults to the ``logs`` directory below the ``datadir``.
``plugins_default``
A comma-separated list of plugins that are enabled by default for all new events.
Defaults to ``pretix.plugins.sendmail,pretix.plugins.statistics``.
@@ -78,8 +71,11 @@ Example::
``auth_backends``
A comma-separated list of available auth backends. Defaults to ``pretix.base.auth.NativeAuthBackend``.
``cookie_domain``
The cookie domain to be set. Defaults to ``None``.
``registration``
Enables or disables the registration of new admin users. Defaults to ``off``.
Enables or disables the registration of new admin users. Defaults to ``on``.
``password_reset``
Enables or disables password reset. Defaults to ``on``.
@@ -97,9 +93,8 @@ Example::
Defaults to ``off``.
``obligatory_2fa``
Enables or disables obligatory usage of two-factor authentication for users of the pretix backend.
Can be ``True`` to make two-factor authentication obligatory for all users or ``staff`` to make it only
obligatory to users with admin permissions. Defaults to ``False``.
Enables or disables obligatory usage of Two-Factor Authentication for users of the pretix backend.
Defaults to ``False``
``trust_x_forwarded_for``
Specifies whether the ``X-Forwarded-For`` header can be trusted. Only set to ``on`` if you have a reverse
@@ -158,7 +153,6 @@ Example::
host=localhost
port=3306
advisory_lock_index=1
disable_server_side_cursors=0
sslmode=require
sslrootcert=/etc/pretix/postgresql-ca.crt
sslcert=/etc/pretix/postgresql-client-crt.crt
@@ -179,11 +173,6 @@ Example::
and are not scoped to a specific database. If you run multiple pretix applications with the same PostgreSQL server,
you should set separate values for this setting (integers up to 256).
``disable_server_side_cursors``
On PostgreSQL pretix might use server side cursors for certain operations. This is generally fine but will break in
specific circumstances, for example when connecting to PostgreSQL through a PGBouncer configured with a transaction
pool mode. Off by default (i.e. by default server side cursors will be used).
``sslmode``, ``sslrootcert``
Connection TLS details for the PostgreSQL database connection. Possible values of ``sslmode`` are ``disable``, ``allow``, ``prefer``, ``require``, ``verify-ca``, and ``verify-full``. ``sslrootcert`` should be the accessible path of the ca certificate. Both values are empty by default.
@@ -288,26 +277,17 @@ Example::
[django]
secret=j1kjps5a5&4ilpn912s7a1!e2h!duz^i3&idu@_907s$wrz@x-
debug=off
passwords_argon2=on
``secret``
The secret to be used by Django for signing and verification purposes. If this
setting is not provided, pretix will generate a random secret on the first start
and will store it in the filesystem for later usage.
``secret_fallback0`` ... ``secret_fallback9``
Prior versions of the secret to be used by Django for signing and verification purposes that will still
be accepted but no longer be used for new signing.
``debug``
Whether or not to run in debug mode. Default is ``False``.
.. WARNING:: Never set this to ``True`` in production!
``passwords_argon``
Use the ``argon2`` algorithm for password hashing. Disable on systems with a small number of CPU cores (currently
less than 8).
``profile``
Enable code profiling for a random subset of requests. Disabled by default, see
:ref:`perf-monitoring` for details.
@@ -369,7 +349,7 @@ to speed up various operations::
The location of redis, as a URL of the form ``redis://[:password]@localhost:6379/0``
or ``unix://[:password]@/path/to/socket.sock?db=0``
``sessions``
``session``
When this is set to ``True``, redis will be used as the session storage.
``sentinels``
@@ -545,4 +525,4 @@ pretix can optionally make use of a GeoIP database for some features. It needs a
.. _GeoAcumen: https://github.com/geoacumen/geoacumen-country
.. _GeoLite2: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
.. _GeoLite2: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data

View File

@@ -1,18 +0,0 @@
.. highlight:: none
.. _`community`:
Community install guides
========================
.. warning:: The guides are maintained by the community and not by the pretix core team. If you encounter any issues with the guides, please report them to the maintainers of the guides. The pretix core team can not provide support for installs using these guides.
Kubernetes
----------
- Helm Chart by techwolf12 - A Helm chart for deploying pretix on Kubernetes. The chart documentation is available on `ArtifactHub <https://artifacthub.io/packages/helm/techwolf12/pretix>`_ and the source code is available on `GitHub <https://github.com/Techwolf12/charts/tree/main/pretix-helm>`_.
Docker
------
- `docker compose setup <https://github.com/ZPascal/pretix-docker-compose>`_ by ZPascal

View File

@@ -19,7 +19,7 @@ You can use ``pip`` to update pretix directly to the development branch. Then, u
(venv)$ pip3 install -U "git+https://github.com/pretix/pretix.git#egg=pretix"
(venv)$ python -m pretix migrate
(venv)$ python -m pretix rebuild
(venv)$ python -m pretix updateassets
(venv)$ python -m pretix updatestyles
# systemctl restart pretix-web pretix-worker
Docker installation

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`_ 12+ database server
* A `PostgreSQL`_ 11+ 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
@@ -231,10 +231,11 @@ The following snippet is an example on how to configure a nginx proxy for pretix
}
}
server {
listen 443 ssl default_server;
listen [::]:443 ipv6only=on ssl default_server;
listen 443 default_server;
listen [::]:443 ipv6only=on default_server;
server_name pretix.mydomain.com;
ssl on;
ssl_certificate /path/to/cert.chain.pem;
ssl_certificate_key /path/to/key.pem;
@@ -275,8 +276,7 @@ Restarting the service can take a few seconds, especially if the update requires
Replace ``stable`` above with a specific version number like ``1.0`` or with ``latest`` for the development
version, if you want to.
Make sure to also read :ref:`update_notes` and the release notes of the version you are updating to. Pay special
attention to the "Runtime and server environment" section of all release notes between your current and new version.
Make sure to also read :ref:`update_notes` and the release notes of the version you are updating to.
.. _`docker_plugininstall`:

View File

@@ -14,4 +14,3 @@ for your needs.
manual_smallscale
dev_version
enterprise
community

View File

@@ -24,7 +24,7 @@ installation guides):
* A python 3.9+ installation
* 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`_ 12+ database server
* A `PostgreSQL`_ 11+ database server
* A `redis`_ server
* A `nodejs`_ installation
@@ -65,7 +65,7 @@ Package dependencies
To build and run pretix, you will need the following debian packages::
# apt-get install git build-essential python3-dev python3-venv python3 python3-pip \
libxml2-dev libxslt1-dev libffi-dev zlib1g-dev libssl-dev \
python3-dev libxml2-dev libxslt1-dev libffi-dev zlib1g-dev libssl-dev \
gettext libpq-dev libjpeg-dev libopenjp2-7-dev
Config file
@@ -120,7 +120,6 @@ Now we will install pretix itself. The following steps are to be executed as the
actually install pretix, we will create a virtual environment to isolate the python packages from your global
python installation::
# sudo -u pretix -s
$ python3 -m venv /var/pretix/venv
$ source /var/pretix/venv/bin/activate
(venv)$ pip3 install -U pip setuptools wheel
@@ -216,10 +215,11 @@ The following snippet is an example on how to configure a nginx proxy for pretix
}
}
server {
listen 443 ssl default_server;
listen [::]:443 ipv6only=on ssl default_server;
listen 443 default_server;
listen [::]:443 ipv6only=on default_server;
server_name pretix.mydomain.com;
ssl on;
ssl_certificate /path/to/cert.chain.pem;
ssl_certificate_key /path/to/key.pem;
@@ -248,14 +248,6 @@ The following snippet is an example on how to configure a nginx proxy for pretix
return 404;
}
location /static/staticfiles.json {
deny all;
return 404;
}
location /static/CACHE/manifest.json {
deny all;
return 404;
}
location /static/ {
alias /var/pretix/venv/lib/python3.11/site-packages/pretix/static.dist/;
access_log off;
@@ -287,16 +279,14 @@ Updates
To upgrade to a new pretix release, pull the latest code changes and run the following commands::
# sudo -u pretix -s
$ source /var/pretix/venv/bin/activate
(venv)$ pip3 install -U --upgrade-strategy eager pretix gunicorn
(venv)$ python -m pretix migrate
(venv)$ python -m pretix rebuild
(venv)$ python -m pretix updateassets
(venv)$ python -m pretix updatestyles
# systemctl restart pretix-web pretix-worker
Make sure to also read :ref:`update_notes` and the release notes of the version you are updating to. Pay special
attention to the "Runtime and server environment" section of all release notes between your current and new version.
Make sure to also read :ref:`update_notes` and the release notes of the version you are updating to.
.. _`manual_plugininstall`:
@@ -332,7 +322,7 @@ Then, proceed like after any plugin installation::
(venv)$ python -m pretix migrate
(venv)$ python -m pretix rebuild
(venv)$ python -m pretix updateassets
(venv)$ python -m pretix updatestyles
# systemctl restart pretix-web pretix-worker
.. _Postfix: https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-as-a-send-only-smtp-server-on-ubuntu-22-04

View File

@@ -103,12 +103,6 @@ pretix_celery_tasks_queued_count
pretix_celery_tasks_queued_age_seconds
The age of the longest-waiting in the worker queue in seconds, labeled with ``queue``.
pretix_logins_successful
Counter. The number of successful backend logins.
pretix_logins_failed
Counter. The number of failed backend logins, labeled with ``reason``.
.. _metric types: https://prometheus.io/docs/concepts/metric_types/
.. _Prometheus: https://prometheus.io/
.. _cProfile: https://docs.python.org/3/library/profile.html

View File

@@ -47,37 +47,5 @@ Or, with a docker installation::
$ docker exec -it pretix.service pretix create_order_transactions
Upgrade to 2023.6.0 or newer
""""""""""""""""""""""""""""
MariaDB and MySQL are no longer supported.
Upgrade to 2023.8.0 or newer
""""""""""""""""""""""""""""
PostgreSQL 11 is now required.
Upgrade to 2023.9.0 or newer
""""""""""""""""""""""""""""
This release includes a migration that changes the `id` column of all core database tables from `integer`
to `bigint`. If you have a large database, the migration step of the upgrade might take significantly longer than
usual, so plan the update accordingly.
The default value for the `registration` setting in `pretix.cfg` has changed to `false`.
Upgrade to 2023.10.0 or newer
"""""""""""""""""""""""""""""
This release includes a migration that changes retroactively fills an `organizer` column in the table
`pretixbase_logentry`. If you have a large database, the migration step of the upgrade might take significantly
longer than usual, so plan the update accordingly.
Upgrade to 2024.7.0 or newer
"""""""""""""""""""""""""""""
This release includes a migration that changes how sales channels are referred on orders.
If you have a large database, the migration step of the upgrade might take significantly longer than usual, so plan
the update accordingly.
.. _blog: https://pretix.eu/about/en/blog/

View File

@@ -249,10 +249,7 @@ You can get three response codes:
Content-Type: application/json
{
"event": {
"name": "Demo Conference",
"slug": "democon"
},
"event": "democon",
"subevent": 23,
"checkinlist": 5
}

View File

@@ -94,9 +94,7 @@ If you want the user to return to your application after the payment is complete
"Plugins". Enable the plugin "Redirection from order page". Then, go to the new page "Settings", then "Redirection".
Enter the base URL of your web application. This will allow you to redirect to pages under this base URL later on.
For example, if you want users to be redirected to ``https://example.org/order/return?tx_id=1234``, you could now
either enter ``https://example.org/order/`` or ``https://example.org/``.
Please note that in the latter case the trailing slash is required, ``https://example.org`` is not allowed to prevent.
Only base URLs with a secure (``https://``) or local (``http://localhost``) origin are permitted.
either enter ``https://example.org`` or ``https://example.org/order/``.
The user will be redirected back to your page instead of pretix' order confirmation page after the payment,
**regardless of whether it was successful or not**. We will append an ``error=…`` query parameter with an error

View File

@@ -1,259 +0,0 @@
.. _rest-autocheckinrules:
Auto check-in rules
===================
This feature requires the bundled ``pretix.plugins.autocheckin`` plugin to be active for the event in order to work properly.
Resource description
--------------------
Auto check-in rules specify that tickets should under specific conditions automatically be considered checked in after
they have been purchased.
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the rule
list integer ID of the check-in list to check the ticket in on. If
``None``, the system will select all matching check-in lists.
mode string ``"placed"`` if the rule should be evaluated right after
an order has been created, ``"paid"`` if the rule should
be evaluated after the order has been fully paid.
all_sales_channels boolean If ``true`` (default), the rule applies to tickets sold on all sales channels.
limit_sales_channels list of strings List of sales channel identifiers the rule should apply to
if ``all_sales_channels`` is ``false``.
all_products boolean If ``true`` (default), the rule affects all products and variations.
limit_products list of integers List of item IDs, if ``all_products`` is not set. If the
product listed here has variations, all variations will be matched.
limit_variations list of integers List of product variation IDs, if ``all_products`` is not set.
The parent product does not need to be part of ``limit_products``.
all_payment_methods boolean If ``true`` (default), the rule applies to tickets paid with all payment methods.
limit_payment_methods list of strings List of payment method identifiers the rule should apply to
if ``all_payment_methods`` is ``false``.
===================================== ========================== =======================================================
.. versionadded:: 2024.7
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/auto_checkin_rules/
Returns a list of all rules configured for an event.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/auto_checkin_rules/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"list": 12345,
"mode": "placed",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"all_products": false,
"limit_products": [2, 3],
"limit_variations": [456],
"all_payment_methods": true,
"limit_payment_methods": []
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the event 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 it.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/auto_checkin_rules/(id)/
Returns information on one rule, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/auto_checkin_rules/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,
"list": 12345,
"mode": "placed",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"all_products": false,
"limit_products": [2, 3],
"limit_variations": [456],
"all_payment_methods": true,
"limit_payment_methods": []
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the rule to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/rule does not exist **or** you have no permission to view it.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/auto_checkin_rules/
Create a new rule.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/auto_checkin_rules/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 166
{
"list": 12345,
"mode": "placed",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"all_products": false,
"limit_products": [2, 3],
"limit_variations": [456],
"all_payment_methods": true,
"limit_payment_methods": []
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"list": 12345,
"mode": "placed",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"all_products": false,
"limit_products": [2, 3],
"limit_variations": [456],
"all_payment_methods": true,
"limit_payment_methods": []
}
:param organizer: The ``slug`` field of the organizer to create a rule for
:param event: The ``slug`` field of the event to create a rule for
:statuscode 201: no error
:statuscode 400: The rule could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create rules.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/auto_checkin_rules/(id)/
Update a rule. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/auto_checkin_rules/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 34
{
"mode": "paid",
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"id": 1,
"list": 12345,
"mode": "placed",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"all_products": false,
"limit_products": [2, 3],
"limit_variations": [456],
"all_payment_methods": true,
"limit_payment_methods": []
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the rule to modify
:statuscode 200: no error
:statuscode 400: The rule could not be modified due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/rule does not exist **or** you have no permission to change it.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/auto_checkin_rules/(id)/
Delete a rule.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/auto_checkin_rules/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the rule to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/rule does not exist **or** you have no permission to change it **or** this rule cannot be deleted since it is currently in use.

View File

@@ -40,11 +40,6 @@ answers list of objects Answers to user
seat objects The assigned seat (or ``null``)
├ id integer Internal ID of the seat instance
├ name string Human-readable seat name
├ zone_name string Name of the zone the seat is in
├ row_name string Name/number of the row the seat is in
├ row_label string Additional label of the row (or ``null``)
├ seat_number string Number of the seat within the row
├ seat_label string Additional label of the seat (or ``null``)
└ seat_guid string Identifier of the seat within the seating plan
===================================== ========================== =======================================================

View File

@@ -23,22 +23,6 @@ position integer An integer, use
is_addon boolean If ``true``, items within this category are not on sale
on their own but the category provides a source for
defining add-ons for other products.
cross_selling_mode string If ``null``, cross-selling is disabled for this category.
If ``"only"``, it is only visible in the cross-selling
step.
If ``"both"``, it is visible on the normal index page
as well.
Only available if ``is_addon`` is ``false``.
cross_selling_condition string Only relevant if ``cross_selling_mode`` is not ``null``.
If ``"always"``, always show in cross-selling step.
If ``"products"``, only show if the cart contains one of
the products listed in ``cross_selling_match_products``.
If ``"discounts"``, only show products that qualify for
a discount according to discount rules.
cross_selling_match_products list of integer Only relevant if ``cross_selling_condition`` is
``"products"``. Internal ID of the items of which at
least one needs to be in the cart for this category to
be shown.
===================================== ========================== =======================================================
@@ -76,10 +60,7 @@ Endpoints
"internal_name": "",
"description": {"en": "Tickets are what you need to get in."},
"position": 1,
"is_addon": false,
"cross_selling_mode": null,
"cross_selling_condition": null,
"cross_selling_match_products": []
"is_addon": false
}
]
}
@@ -121,10 +102,7 @@ Endpoints
"internal_name": "",
"description": {"en": "Tickets are what you need to get in."},
"position": 1,
"is_addon": false,
"cross_selling_mode": null,
"cross_selling_condition": null,
"cross_selling_match_products": []
"is_addon": false
}
:param organizer: The ``slug`` field of the organizer to fetch
@@ -152,10 +130,7 @@ Endpoints
"internal_name": "",
"description": {"en": "Tickets are what you need to get in."},
"position": 1,
"is_addon": false,
"cross_selling_mode": null,
"cross_selling_condition": null,
"cross_selling_match_products": []
"is_addon": false
}
**Example response**:
@@ -172,10 +147,7 @@ Endpoints
"internal_name": "",
"description": {"en": "Tickets are what you need to get in."},
"position": 1,
"is_addon": false,
"cross_selling_mode": null,
"cross_selling_condition": null,
"cross_selling_match_products": []
"is_addon": false
}
:param organizer: The ``slug`` field of the organizer of the event to create a category for
@@ -221,10 +193,7 @@ Endpoints
"internal_name": "",
"description": {"en": "Tickets are what you need to get in."},
"position": 1,
"is_addon": true,
"cross_selling_mode": null,
"cross_selling_condition": null,
"cross_selling_match_products": []
"is_addon": true
}
:param organizer: The ``slug`` field of the organizer to modify

View File

@@ -31,7 +31,6 @@ Checking a ticket in
This endpoint supports passing multiple check-in lists to perform a multi-event scan. However, each check-in list
passed needs to be from a distinct event.
:query string expand: Expand a field inside the ``position`` object into a full object. Currently ``subevent``, ``item``, ``variation``, and ``answers.question`` are supported. Can be passed multiple times.
:<json string secret: Scanned QR code corresponding to the ``secret`` attribute of a ticket.
:<json string source_type: Type of source the ``secret`` was obtained form. Defaults to ``"barcode"``.
:<json array lists: List of check-in list IDs to search on. No two check-in lists may be from the same event.
@@ -64,7 +63,6 @@ Checking a ticket in
``checkin_attention`` flag set. (3) If ``attendee_name`` is empty, it may automatically fall
back to values from a parent product or from invoice addresses.
:>json boolean require_attention: Whether or not the ``require_attention`` flag is set on the item or order.
:>json list checkin_texts: List of additional texts to show to the user.
:>json object list: Excerpt of information about the matching :ref:`check-in list <rest-checkinlists>` (if any was found),
including the attributes ``id``, ``name``, ``event``, ``subevent``, and ``include_pending``.
:>json object questions: List of questions to be answered for check-in, only set on status ``"incomplete"``.
@@ -105,7 +103,6 @@ Checking a ticket in
},
"require_attention": false,
"checkin_texts": [],
"list": {
"id": 1,
"name": "Default check-in list",
@@ -128,7 +125,6 @@ Checking a ticket in
},
"require_attention": false,
"checkin_texts": [],
"list": {
"id": 1,
"name": "Default check-in list",
@@ -146,7 +142,6 @@ Checking a ticket in
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": true,
"show_during_checkin": true,
"options": [
{
"id": 1,
@@ -183,8 +178,7 @@ Checking a ticket in
"status": "error",
"reason": "invalid",
"reason_explanation": null,
"require_attention": false,
"checkin_texts": []
"require_attention": false
}
**Example error response (known, but invalid ticket)**:
@@ -199,7 +193,6 @@ Checking a ticket in
"reason": "unpaid",
"reason_explanation": null,
"require_attention": false,
"checkin_texts": [],
"list": {
"id": 1,
"name": "Default check-in list",
@@ -224,7 +217,6 @@ Checking a ticket in
* ``rules`` - Check-in prevented by a user-defined rule.
* ``ambiguous`` - Multiple tickets match scan, rejected.
* ``revoked`` - Ticket code has been revoked.
* ``unapproved`` - Order has not yet been approved.
* ``error`` - Internal error.
In case of reason ``rules`` and ``invalid_time``, there might be an additional response field ``reason_explanation``

View File

@@ -31,23 +31,18 @@ subevent integer ID of the date
position_count integer Number of tickets that match this list (read-only).
checkin_count integer Number of check-ins performed on this list (read-only).
include_pending boolean If ``true``, the check-in list also contains tickets from orders in pending state.
auto_checkin_sales_channels list of strings All items on the check-in list will be automatically marked as checked-in when purchased through any of the listed sales channels.
allow_multiple_entries boolean If ``true``, subsequent scans of a ticket on this list should not show a warning but instead be stored as an additional check-in.
allow_entry_after_exit boolean If ``true``, subsequent scans of a ticket on this list are valid if the last scan of the ticket was an exit scan.
rules object Custom check-in logic. The contents of this field are currently not considered a stable API and modifications through the API are highly discouraged.
exit_all_at datetime Automatically check out (i.e. perform an exit scan) at this point in time. After this happened, this property will automatically be set exactly one day into the future. Note that this field is considered "internal configuration" and if you pull the list with ``If-Modified-Since``, the daily change in this field will not trigger a response.
addon_match boolean If ``true``, tickets on this list can be redeemed by scanning their parent ticket if this still leads to an unambiguous match.
ignore_in_statistics boolean If ``true``, check-ins on this list will be ignored in most reporting features.
consider_tickets_used boolean If ``true`` (default), tickets checked in on this list will be considered "used" by other functionality, i.e. when checking if they can still be canceled.
===================================== ========================== =======================================================
.. versionchanged:: 4.12
The ``addon_match`` attribute has been added.
.. versionchanged:: 2023.9
The ``ignore_in_statistics`` and ``consider_tickets_used`` attributes have been added.
Endpoints
---------
@@ -89,7 +84,10 @@ Endpoints
"allow_entry_after_exit": true,
"exit_all_at": null,
"rules": {},
"addon_match": false
"addon_match": false,
"auto_checkin_sales_channels": [
"pretixpos"
]
}
]
}
@@ -141,7 +139,10 @@ Endpoints
"allow_entry_after_exit": true,
"exit_all_at": null,
"rules": {},
"addon_match": false
"addon_match": false,
"auto_checkin_sales_channels": [
"pretixpos"
]
}
:param organizer: The ``slug`` field of the organizer to fetch
@@ -238,7 +239,10 @@ Endpoints
"subevent": null,
"allow_multiple_entries": false,
"allow_entry_after_exit": true,
"addon_match": false
"addon_match": false,
"auto_checkin_sales_channels": [
"pretixpos"
]
}
**Example response**:
@@ -260,7 +264,10 @@ Endpoints
"subevent": null,
"allow_multiple_entries": false,
"allow_entry_after_exit": true,
"addon_match": false
"addon_match": false,
"auto_checkin_sales_channels": [
"pretixpos"
]
}
:param organizer: The ``slug`` field of the organizer of the event/item to create a list for
@@ -312,7 +319,10 @@ Endpoints
"subevent": null,
"allow_multiple_entries": false,
"allow_entry_after_exit": true,
"addon_match": false
"addon_match": false,
"auto_checkin_sales_channels": [
"pretixpos"
]
}
:param organizer: The ``slug`` field of the organizer to modify
@@ -325,7 +335,7 @@ Endpoints
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/checkinlist/(id)/
Delete a check-in list. **Note that this also deletes the information on all check-ins performed via this list.**
Delete a check-in list. Note that this also deletes the information on all check-ins performed via this list.
**Example request**:
@@ -482,7 +492,7 @@ Order position endpoints
``attendee_name,positionid``
:query string order: Only return positions of the order with the given order code
:query string search: Fuzzy search matching the attendee name, order code, invoice address name as well as to the beginning of the secret.
:query string expand: Expand a field into a full object. Currently ``subevent``, ``item``, ``variation``, and ``answers.question`` are supported. Can be passed multiple times.
:query string expand: Expand a field into a full object. Currently only ``subevent``, ``item``, and ``variation`` are supported. Can be passed multiple times.
:query integer item: Only return positions with the purchased item matching the given ID.
:query integer item__in: Only return positions with the purchased item matching one of the given comma-separated IDs.
:query integer variation: Only return positions with the purchased item variation matching the given ID.
@@ -616,8 +626,7 @@ Order position endpoints
set this to ``false``. In that case, questions will just be ignored. Defaults
to ``true``.
:<json boolean canceled_supported: When this parameter is set to ``true``, the response code ``canceled`` may be
returned. Otherwise, canceled orders will return ``unpaid``. (**Deprecated**, in
the future, this will be ignored and ``canceled`` may always be returned.)
returned. Otherwise, canceled orders will return ``unpaid``.
:<json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
:<json boolean force: Specifies that the check-in should succeed regardless of revoked barcode, previous check-ins or required
questions that have not been filled. This is usually used to upload offline scans that already happened,
@@ -691,7 +700,6 @@ Order position endpoints
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": true,
"show_during_checkin": true,
"options": [
{
"id": 1,
@@ -744,7 +752,6 @@ Order position endpoints
* ``rules`` - Check-in prevented by a user-defined rule.
* ``ambiguous`` - Multiple tickets match scan, rejected.
* ``revoked`` - Ticket code has been revoked.
* ``unapproved`` - Order has not yet been approved.
In case of reason ``rules`` or ``invalid_time``, there might be an additional response field ``reason_explanation``
with a human-readable description of the violated rules. However, that field can also be missing or be ``null``.
@@ -760,4 +767,4 @@ Order position endpoints
:statuscode 404: The requested order position or check-in list does not exist.
.. _security issues: https://pretix.eu/about/de/blog/20220705-release-4111/
.. _security issues: https://pretix.eu/about/de/blog/20220705-release-4111/

View File

@@ -19,7 +19,6 @@ external_identifier string External ID of
the API, but is read-only for customers created through a
SSO integration.
email string Customer email address
phone string Customer phone number
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
@@ -40,10 +39,6 @@ password string Can only be set
Passwords can now be set through the API during customer creation.
.. versionchanged:: 2024.3
The attribute ``phone`` has been added.
Endpoints
---------
@@ -76,7 +71,6 @@ Endpoints
"identifier": "8WSAJCJ",
"external_identifier": null,
"email": "customer@example.org",
"phone": "+493012345678",
"name": "John Doe",
"name_parts": {
"_scheme": "full",
@@ -124,7 +118,6 @@ Endpoints
"identifier": "8WSAJCJ",
"external_identifier": null,
"email": "customer@example.org",
"phone": "+493012345678",
"name": "John Doe",
"name_parts": {
"_scheme": "full",
@@ -162,7 +155,6 @@ Endpoints
{
"email": "test@example.org",
"phone": "+493012345678",
"password": "verysecret",
"send_email": true
}
@@ -179,7 +171,6 @@ Endpoints
"identifier": "8WSAJCJ",
"external_identifier": null,
"email": "test@example.org",
"phone": "+493012345678",
...
}
@@ -224,7 +215,6 @@ Endpoints
"identifier": "8WSAJCJ",
"external_identifier": null,
"email": "test@example.org",
"phone": "+493012345678",
}
@@ -259,7 +249,6 @@ Endpoints
"identifier": "8WSAJCJ",
"external_identifier": null,
"email": null,
"phone": null,
}

View File

@@ -20,12 +20,8 @@ id integer Internal ID
active boolean The discount will be ignored if this is ``false``
internal_name string A name for the rule used in the backend
position integer An integer, used for sorting the rules which are applied in order
all_sales_channels boolean If ``true`` (default), the discount is available on all sales channels
that support discounts.
limit_sales_channels list of strings List of sales channel identifiers the discount is available on
if ``all_sales_channels`` is ``false``.
sales_channels list of strings **DEPRECATED.** Legacy interface, use ``all_sales_channels``
and ``limit_sales_channels`` instead.
sales_channels list of strings Sales channels this discount is available on, such as
``"web"`` or ``"resellers"``. Defaults to ``["web"]``.
available_from datetime The first date time at which this discount can be applied
(or ``null``).
available_until datetime The last date time at which this discount can be applied
@@ -99,8 +95,6 @@ Endpoints
"active": true,
"internal_name": "3 for 2",
"position": 1,
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
@@ -157,8 +151,6 @@ Endpoints
"active": true,
"internal_name": "3 for 2",
"position": 1,
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
@@ -201,8 +193,6 @@ Endpoints
"active": true,
"internal_name": "3 for 2",
"position": 1,
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
@@ -234,8 +224,6 @@ Endpoints
"active": true,
"internal_name": "3 for 2",
"position": 1,
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
@@ -296,8 +284,6 @@ Endpoints
"active": false,
"internal_name": "3 for 2",
"position": 1,
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,

View File

@@ -36,8 +36,6 @@ geo_lon float Longitude of th
has_subevents boolean ``true`` if the event series feature is active for this
event. Cannot change after event is created.
meta_data object Values set for organizer-specific meta data parameters.
The allowed keys need to be set up as meta properties
in the organizer configuration.
plugins list A list of package names of the enabled plugins for this
event.
seating_plan integer If reserved seating is in use, the ID of a seating
@@ -49,11 +47,8 @@ item_meta_properties object Item-specific m
valid_keys object Cryptographic keys for non-default signature schemes.
For performance reason, value is omitted in lists and
only contained in detail views. Value can be cached.
all_sales_channels boolean If ``true`` (default), the event is available on all sales channels.
limit_sales_channels list of strings List of sales channel identifiers the event is available on
if ``all_sales_channels`` is ``false``.
sales_channels list of strings **DEPRECATED.** Legacy interface, use ``all_sales_channels``
and ``limit_sales_channels`` instead.
sales_channels list A list of sales channels this event is available for
sale on.
public_url string The public, customer-facing URL of the event (read-only).
===================================== ========================== =======================================================
@@ -134,13 +129,11 @@ Endpoints
"pretix.plugins.paypal",
"pretix.plugins.ticketoutputpdf"
],
"all_sales_channels": false,
"limit_sales_channels": [
"sales_channels": [
"web",
"pretixpos",
"resellers"
],
"sales_channels": [],
"public_url": "https://pretix.eu/bigevents/sampleconf/"
}
]
@@ -230,8 +223,6 @@ Endpoints
"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUNvd0JRWURLMlZ3QXlFQTdBRDcvdkZBMzNFc1k0ejJQSHI3aVpQc1o4bjVkaDBhalA4Z3l6Tm1tSXM9Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo="
]
},
"all_sales_channels": true,
"limit_sales_channels": [],
"sales_channels": [
"web",
"pretixpos",
@@ -289,8 +280,11 @@ Endpoints
"pretix.plugins.stripe",
"pretix.plugins.paypal"
],
"all_sales_channels": true,
"limit_sales_channels": []
"sales_channels": [
"web",
"pretixpos",
"resellers"
]
}
**Example response**:
@@ -326,8 +320,6 @@ Endpoints
"pretix.plugins.stripe",
"pretix.plugins.paypal"
],
"all_sales_channels": true,
"limit_sales_channels": [],
"sales_channels": [
"web",
"pretixpos",
@@ -351,8 +343,8 @@ Endpoints
Creates a new event with properties as set in the request body. The properties that are copied are: ``is_public``,
``testmode``, ``has_subevents``, settings, plugin settings, items, variations, add-ons, quotas, categories, tax rules, questions.
If the ``plugins``, ``has_subevents``, ``meta_data`` 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.
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.
@@ -393,8 +385,11 @@ Endpoints
"pretix.plugins.stripe",
"pretix.plugins.paypal"
],
"all_sales_channels": true,
"limit_sales_channels": []
"sales_channels": [
"web",
"pretixpos",
"resellers"
]
}
**Example response**:
@@ -430,8 +425,6 @@ Endpoints
"pretix.plugins.stripe",
"pretix.plugins.paypal"
],
"all_sales_channels": true,
"limit_sales_channels": [],
"sales_channels": [
"web",
"pretixpos",
@@ -507,8 +500,6 @@ Endpoints
"pretix.plugins.paypal",
"pretix.plugins.pretixdroid"
],
"all_sales_channels": true,
"limit_sales_channels": [],
"sales_channels": [
"web",
"pretixpos",
@@ -574,8 +565,6 @@ organizer level.
.. warning:: This API is intended for advanced users. Even though we take care to validate your input, you will be
able to break your event using this API by creating situations of conflicting settings. Please take care.
.. note:: When authenticating with :ref:`rest-deviceauth`, only a limited subset of settings is available.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/settings/
Get current values of event settings.

View File

@@ -1,7 +1,5 @@
.. spelling:word-list:: checkin
.. _rest-exporters:
Data exporters
==============

View File

@@ -96,8 +96,6 @@ Endpoints
:query integer page: The page number in case of a multi-page result set, default is 1
:query string secret: Only show gift cards with the given secret.
:query string value: Only show gift cards with the given value.
:query boolean expired: Filter for gift cards that are (not) expired.
:query boolean testmode: Filter for gift cards that are (not) in test mode.
:query boolean include_accepted: Also show gift cards issued by other organizers that are accepted by this organizer.
:query string expand: If you pass ``"owner_ticket"``, the respective field will be shown as a nested value instead of just an ID.

View File

@@ -30,7 +30,6 @@ at :ref:`plugin-docs`.
checkinlists
waitinglist
customers
saleschannels
membershiptypes
memberships
giftcards
@@ -41,10 +40,7 @@ at :ref:`plugin-docs`.
webhooks
seatingplans
exporters
scheduled_exports
shredders
sendmail_rules
auto_checkin_rules
billing_invoices
billing_var
seats
billing_var

View File

@@ -97,7 +97,6 @@ lines list of objects The actual invo
├ gross_value money (string) Price including taxes
├ tax_value money (string) Tax amount included
├ tax_name string Name of used tax rate (e.g. "VAT")
├ tax_code string Codified reason for tax rate (or ``null``), see :ref:`rest-taxcodes`.
└ tax_rate decimal (string) Used tax rate
foreign_currency_display string If the invoice should also show the total and tax
amount in a different currency, this contains the
@@ -127,10 +126,6 @@ internal_reference string Customer's refe
The ``event`` attribute has been added. The organizer-level endpoint has been added.
.. versionchanged:: 2024.8
The ``tax_code`` attribute has been added.
List of all invoices
--------------------
@@ -208,7 +203,6 @@ List of all invoices
"gross_value": "23.00",
"tax_value": "0.00",
"tax_name": "VAT",
"tax_code": "S/standard",
"tax_rate": "0.00"
}
],
@@ -223,9 +217,6 @@ List of all invoices
:query boolean is_cancellation: If set to ``true`` or ``false``, only invoices with this value for the field
``is_cancellation`` will be returned.
:query string order: If set, only invoices belonging to the order with the given order code will be returned.
This parameter may be given multiple times. In this case, all invoices matching one of the inputs will be returned.
:query string number: If set, only invoices with the given invoice number will be returned.
This parameter may be given multiple times. In this case, all invoices matching one of the inputs will be returned.
:query string refers: If set, only invoices referring to the given invoice will be returned.
:query string locale: If set, only invoices with the given locale will be returned.
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``date`` and
@@ -348,7 +339,6 @@ Fetching individual invoices
"gross_value": "23.00",
"tax_value": "0.00",
"tax_name": "VAT",
"tax_code": "S/standard",
"tax_rate": "0.00"
}
],
@@ -359,12 +349,12 @@ Fetching individual invoices
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param number: The ``number`` field of the invoice to fetch
:param invoice_no: The ``invoice_no`` field of the invoice to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/invoices/(number)/download/
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/invoices/(invoice_no)/download/
Download an invoice in PDF format.
@@ -391,7 +381,7 @@ Fetching individual invoices
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param number: The ``number`` field of the invoice to fetch
:param invoice_no: The ``invoice_no`` field of the invoice to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
@@ -404,7 +394,7 @@ Modifying invoices
Invoices cannot be edited directly, but the following actions can be triggered:
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(number)/reissue/
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(invoice_no)/reissue/
Cancels the invoice and creates a new one.
@@ -426,13 +416,13 @@ Invoices cannot be edited directly, but the following actions can be triggered:
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param number: The ``number`` field of the invoice to reissue
:param invoice_no: The ``invoice_no`` field of the invoice to reissue
:statuscode 200: no error
:statuscode 400: The invoice has already been canceled
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(number)/regenerate/
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(invoice_no)/regenerate/
Re-generates the invoice from order data.
@@ -454,7 +444,7 @@ Invoices cannot be edited directly, but the following actions can be triggered:
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param number: The ``number`` field of the invoice to regenerate
:param invoice_no: The ``invoice_no`` field of the invoice to regenerate
:statuscode 200: no error
:statuscode 400: The invoice has already been canceled
:statuscode 401: Authentication failure

View File

@@ -18,8 +18,6 @@ default_price money (string) The price set d
price money (string) The price used for this variation. This is either the
same as ``default_price`` if that value is set or equal
to the item's ``default_price`` (read-only).
free_price_suggestion money (string) A suggested price, used as a default value if
``Item.free_price`` is set (or ``null``).
original_price money (string) An original price, shown for comparison, not used
for price calculations (or ``null``).
active boolean If ``false``, this variation will not be sold or shown.
@@ -29,8 +27,6 @@ position integer An integer, use
checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if such
a variation is being scanned.
checkin_text string Text that will be shown if a ticket of this type is
scanned (or ``null``).
require_approval boolean If ``true``, orders with this variation will need to be
approved by the event organizer before they can be
paid.
@@ -38,26 +34,15 @@ require_membership boolean If ``true``, bo
require_membership_hidden boolean If ``true`` and ``require_membership`` is set, this variation will
be hidden from users without a valid membership.
require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
all_sales_channels boolean If ``true`` (default), the variation is available on all sales channels.
limit_sales_channels list of strings List of sales channel identifiers the variation is available on
if ``all_sales_channels`` is ``false``.
sales_channels list of strings Sales channels this variation is available on, such as
``"web"`` or ``"resellers"``. Defaults to all existing sales channels.
The item-level list takes precedence, i.e. a sales
channel needs to be on both lists for the variation to be
available (unless ``all_sales_channels`` is used).
sales_channels list of strings **DEPRECATED.** Legacy interface, use ``all_sales_channels``
and ``limit_sales_channels`` instead.
channel needs to be on both lists for the item to be
available.
available_from datetime The first date time at which this variation can be bought
(or ``null``).
available_from_mode string If ``hide`` (the default), this variation is hidden in the shop
if unavailable due to the available_from setting.
If ``info``, the variation is visible, but can't be purchased,
and a note explaining the unavailability is displayed.
available_until datetime The last date time at which this variation can be bought
(or ``null``).
available_until_mode string If ``hide`` (the default), this variation is hidden in the shop
if unavailable due to the available_until setting.
If ``info``, the variation is visible, but can't be purchased,
and a note explaining the unavailability is displayed.
hide_without_voucher boolean If ``true``, this variation is only shown during the voucher
redemption process, but not in the normal shop
frontend.
@@ -68,12 +53,6 @@ meta_data object Values set for
The ``meta_data`` and ``checkin_attention`` attributes have been added.
.. versionchanged:: 2023.10
The ``free_price_suggestion`` attribute has been added.
The ``checkin_text`` attribute has been added.
Endpoints
---------
@@ -109,18 +88,13 @@ Endpoints
},
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": {
"en": "Test2"
@@ -129,7 +103,6 @@ Endpoints
"default_price": "223.00",
"price": 223.0,
"original_price": null,
"free_price_suggestion": null,
"meta_data": {}
},
{
@@ -139,32 +112,20 @@ Endpoints
},
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": {},
"position": 1,
"default_price": "223.00",
"price": 223.0,
"original_price": null,
"free_price_suggestion": null,
"default_price": null,
"price": 15.0,
"meta_data": {}
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string search: Filter the list by the value of the variation (substring search).
:query boolean active: If set to ``true`` or ``false``, only items with this value for the field ``active`` will be
returned.
:param organizer: The ``slug`` field of the organizer to fetch
@@ -202,21 +163,15 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"position": 0,
@@ -249,17 +204,13 @@ Endpoints
"default_price": "10.00",
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"position": 0,
@@ -280,21 +231,15 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"position": 0,
@@ -346,21 +291,15 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"free_price_suggestion": null,
"active": false,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"position": 1,

View File

@@ -29,8 +29,6 @@ free_price boolean If ``true``,
they buy the product (however, the price can't be set
lower than the price defined by ``default_price`` or
otherwise).
free_price_suggestion money (string) A suggested price, used as a default value if
``free_price`` is set (or ``null``).
tax_rate decimal (string) The VAT rate to be applied for this item (read-only,
set through ``tax_rule``).
tax_rule integer The internal ID of the applied tax rule (or ``null``).
@@ -46,33 +44,15 @@ personalized boolean ``true`` for
position integer An integer, used for sorting
picture file A product picture to be displayed in the shop
(can be ``null``).
all_sales_channels boolean If ``true`` (default), the item is available on all sales channels.
limit_sales_channels list of strings List of sales channel identifiers the item is available on
if ``all_sales_channels`` is ``false``.
sales_channels list of strings **DEPRECATED.** Legacy interface, use ``all_sales_channels``
and ``limit_sales_channels`` instead.
sales_channels list of strings Sales channels this product is available on, such as
``"web"`` or ``"resellers"``. Defaults to ``["web"]``.
available_from datetime The first date time at which this item can be bought
(or ``null``).
available_from_mode string If ``hide`` (the default), this item is hidden in the shop
if unavailable due to the ``available_from`` setting.
If ``info``, the item is visible, but can't be purchased,
and a note explaining the unavailability is displayed.
available_until datetime The last date time at which this item can be bought
(or ``null``).
available_until_mode string If ``hide`` (the default), this item is hidden in the shop
if unavailable due to the ``available_until`` setting.
If ``info``, the item is visible, but can't be purchased,
and a note explaining the unavailability is displayed.
hidden_if_available integer **DEPRECATED** The internal ID of a quota object, or ``null``. If
hidden_if_available integer The internal ID of a quota object, or ``null``. If
set, this item won't be shown publicly as long as this
quota is available.
hidden_if_item_available integer The internal ID of a different item, or ``null``. If
set, this item won't be shown publicly as long as this
other item is available.
hidden_if_item_available_mode string If ``hide`` (the default), this item is hidden in the shop
if unavailable due to the ``hidden_if_item_available`` setting.
If ``info``, the item is visible, but can't be purchased,
and a note explaining the unavailability is displayed.
require_voucher boolean If ``true``, this item can only be bought using a
voucher that is specifically assigned to this item.
hide_without_voucher boolean If ``true``, this item is only shown during the voucher
@@ -89,8 +69,6 @@ max_per_order integer This product
checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if such
a product is being scanned.
checkin_text string Text that will be shown if a ticket of this type is
scanned (or ``null``).
original_price money (string) An original price, shown for comparison, not used
for price calculations (or ``null``).
require_approval boolean If ``true``, orders with this product will need to be
@@ -145,8 +123,6 @@ variations list of objects A list with o
├ price money (string) The price used for this variation. This is either the
same as ``default_price`` if that value is set or equal
to the item's ``default_price``.
├ free_price_suggestion money (string) A suggested price, used as a default value if
``free_price`` is set (or ``null``).
├ original_price money (string) An original price, shown for comparison, not used
for price calculations (or ``null``).
├ active boolean If ``false``, this variation will not be sold or shown.
@@ -154,8 +130,6 @@ variations list of objects A list with o
├ checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if such
a variation is being scanned.
├ checkin_text string Text that will be shown if a ticket of this type is
scanned (or ``null``).
├ require_approval boolean If ``true``, orders with this variation will need to be
approved by the event organizer before they can be
paid.
@@ -164,26 +138,15 @@ variations list of objects A list with o
be hidden from users without a valid membership.
├ require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
Markdown syntax or can be ``null``.
all_sales_channels boolean If ``true`` (default), the variation is available on all sales channels.
├ limit_sales_channels list of strings List of sales channel identifiers the variation is available on
if ``all_sales_channels`` is ``false``.
├ sales_channels list of strings Sales channels this variation is available on, such as
``"web"`` or ``"resellers"``. Defaults to all existing sales channels.
The item-level list takes precedence, i.e. a sales
channel needs to be on both lists for the variation to be
available (unless ``all_sales_channels`` is used).
├ sales_channels list of strings **DEPRECATED.** Legacy interface, use ``all_sales_channels``
and ``limit_sales_channels`` instead.
channel needs to be on both lists for the item to be
available.
├ available_from datetime The first date time at which this variation can be bought
(or ``null``).
├ available_from_mode string If ``hide`` (the default), this variation is hidden in the shop
if unavailable due to the ``available_from`` setting.
If ``info``, the variation is visible, but can't be purchased,
and a note explaining the unavailability is displayed.
├ available_until datetime The last date time at which this variation can be bought
(or ``null``).
├ available_until_mode string If ``hide`` (the default), this variation is hidden in the shop
if unavailable due to the ``available_until`` setting.
If ``info``, the variation is visible, but can't be purchased,
and a note explaining the unavailability is displayed.
├ hide_without_voucher boolean If ``true``, this variation is only shown during the voucher
redemption process, but not in the normal shop
frontend.
@@ -233,20 +196,6 @@ meta_data object Values set fo
The ``media_policy`` and ``media_type`` attributes have been added.
.. versionchanged:: 2023.10
The ``checkin_text`` and ``variations[x].checkin_text`` attributes have been added.
The ``free_price_suggestion`` and ``variations[x].free_price_suggestion`` attributes have been added.
.. versionchanged:: 2023.10
The ``hidden_if_item_available`` attributes has been added, the ``hidden_if_available`` attribute has been
deprecated.
.. versionchanged:: 2025.01
The ``hidden_if_item_available_mode`` attributes has been added.
Notes
-----
@@ -290,8 +239,6 @@ Endpoints
"id": 1,
"name": {"en": "Standard ticket"},
"internal_name": "",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"default_price": "23.00",
"original_price": null,
@@ -299,7 +246,6 @@ Endpoints
"active": true,
"description": null,
"free_price": false,
"free_price_suggestion": null,
"tax_rate": "0.00",
"tax_rule": 1,
"admission": false,
@@ -311,19 +257,14 @@ Endpoints
"position": 0,
"picture": null,
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hidden_if_available": null,
"hidden_if_item_available": null,
"hidden_if_item_available_mode": "hide",
"require_voucher": false,
"hide_without_voucher": false,
"allow_cancel": true,
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
"checkin_text": null,
"has_variations": false,
"generate_tickets": null,
"allow_waitinglist": true,
@@ -350,20 +291,14 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"meta_data": {},
@@ -374,20 +309,14 @@ Endpoints
"default_price": null,
"price": "23.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"meta_data": {},
@@ -401,7 +330,6 @@ Endpoints
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string search: Filter the list by internal name or name of the item (substring search).
:query boolean active: If set to ``true`` or ``false``, only items with this value for the field ``active`` will be
returned.
:query integer category: If set to the ID of a category, only items within that category will be returned.
@@ -442,8 +370,6 @@ Endpoints
"id": 1,
"name": {"en": "Standard ticket"},
"internal_name": "",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"default_price": "23.00",
"original_price": null,
@@ -451,7 +377,6 @@ Endpoints
"active": true,
"description": null,
"free_price": false,
"free_price_suggestion": null,
"tax_rate": "0.00",
"tax_rule": 1,
"admission": false,
@@ -463,12 +388,8 @@ Endpoints
"position": 0,
"picture": null,
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hidden_if_available": null,
"hidden_if_item_available": null,
"hidden_if_item_available_mode": "hide",
"require_voucher": false,
"hide_without_voucher": false,
"allow_cancel": true,
@@ -478,7 +399,6 @@ Endpoints
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
"checkin_text": null,
"has_variations": false,
"require_approval": false,
"require_bundling": false,
@@ -502,21 +422,15 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
"description": null,
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"meta_data": {},
"position": 0
@@ -526,20 +440,14 @@ Endpoints
"default_price": null,
"price": "23.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"meta_data": {},
@@ -574,15 +482,13 @@ Endpoints
"id": 1,
"name": {"en": "Standard ticket"},
"internal_name": "",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"default_price": "23.00",
"original_price": null,
"category": null,
"active": true,
"description": null,
"free_price": false,
"free_price_suggestion": null,
"tax_rate": "0.00",
"tax_rule": 1,
"admission": false,
@@ -594,12 +500,8 @@ Endpoints
"position": 0,
"picture": null,
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hidden_if_available": null,
"hidden_if_item_available": null,
"hidden_if_item_available_mode": "hide",
"require_voucher": false,
"hide_without_voucher": false,
"allow_cancel": true,
@@ -609,7 +511,6 @@ Endpoints
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_bundling": false,
"require_membership": false,
@@ -632,19 +533,14 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"meta_data": {},
@@ -655,19 +551,14 @@ Endpoints
"default_price": null,
"price": "23.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"meta_data": {},
@@ -690,8 +581,6 @@ Endpoints
"id": 1,
"name": {"en": "Standard ticket"},
"internal_name": "",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"default_price": "23.00",
"original_price": null,
@@ -699,7 +588,6 @@ Endpoints
"active": true,
"description": null,
"free_price": false,
"free_price_suggestion": null,
"tax_rate": "0.00",
"tax_rule": 1,
"admission": false,
@@ -711,12 +599,8 @@ Endpoints
"position": 0,
"picture": null,
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hidden_if_available": null,
"hidden_if_item_available": null,
"hidden_if_item_available_mode": "hide",
"require_voucher": false,
"hide_without_voucher": false,
"allow_cancel": true,
@@ -726,7 +610,6 @@ Endpoints
"allow_waitinglist": true,
"show_quota_left": null,
"checkin_attention": false,
"checkin_text": null,
"has_variations": true,
"require_approval": false,
"require_bundling": false,
@@ -750,20 +633,14 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"meta_data": {},
@@ -774,20 +651,14 @@ Endpoints
"default_price": null,
"price": "23.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"meta_data": {},
@@ -841,8 +712,6 @@ Endpoints
"id": 1,
"name": {"en": "Ticket"},
"internal_name": "",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"default_price": "25.00",
"original_price": null,
@@ -850,7 +719,6 @@ Endpoints
"active": true,
"description": null,
"free_price": false,
"free_price_suggestion": null,
"tax_rate": "0.00",
"tax_rule": 1,
"admission": false,
@@ -862,12 +730,8 @@ Endpoints
"position": 0,
"picture": null,
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hidden_if_available": null,
"hidden_if_item_available": null,
"hidden_if_item_available_mode": "hide",
"require_voucher": false,
"hide_without_voucher": false,
"generate_tickets": null,
@@ -877,7 +741,6 @@ Endpoints
"min_per_order": null,
"max_per_order": null,
"checkin_attention": false,
"checkin_text": null,
"has_variations": true,
"require_approval": false,
"require_bundling": false,
@@ -901,20 +764,14 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"meta_data": {},
@@ -925,20 +782,14 @@ Endpoints
"default_price": null,
"price": "23.00",
"original_price": null,
"free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"require_membership": false,
"require_membership_types": [],
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"sales_channels": ["web"],
"available_from": null,
"available_from_mode": "hide",
"available_until": null,
"available_until_mode": "hide",
"hide_without_voucher": false,
"description": null,
"meta_data": {},

View File

@@ -42,14 +42,10 @@ payment_date date **DEPRECATED AN
payment_provider string **DEPRECATED AND INACCURATE** Payment provider used for this order
total money (string) Total value of this order
comment string Internal comment on this order
api_meta object Meta data for that order. Only available through API, no guarantees
on the content structure. You can use this to save references to your system.
custom_followup_at date Internal date for a custom follow-up action
checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if a ticket
of this order is scanned.
checkin_text string Text that will be shown if a ticket of this order is
scanned (or ``null``).
invoice_address object Invoice address information (can be ``null``)
├ last_modified datetime Last modification date of the address
├ company string Customer company name
@@ -84,7 +80,6 @@ fees list of objects List of fees in
├ tax_rate decimal (string) VAT rate applied for this fee
├ tax_value money (string) VAT included in this fee
├ tax_rule integer The ID of the used tax rule (or ``null``)
├ tax_code string Codified reason for tax rate (or ``null``), see :ref:`rest-taxcodes`.
└ canceled boolean Whether or not this fee has been canceled.
downloads list of objects List of ticket download options for order-wise ticket
downloading. This might be a multi-page PDF or a ZIP
@@ -105,10 +100,6 @@ url string The full URL to
payments list of objects List of payment processes (see below)
refunds list of objects List of refund processes (see below)
last_modified datetime Last modification of this object
cancellation_date datetime Time of order cancellation (or ``null``). **Note**:
Will not be set for partial cancellations and is not
reliable for orders that have been cancelled,
reactivated and cancelled again.
===================================== ========================== =======================================================
@@ -144,25 +135,6 @@ cancellation_date datetime Time of order c
The ``event`` attribute has been added. The organizer-level endpoint has been added.
.. versionchanged:: 2023.9
The ``customer`` query parameter has been added.
.. versionchanged:: 2023.10
The ``checkin_text`` attribute has been added.
.. versionchanged:: 2024.1
The ``expires`` attribute can now be passed during order creation.
.. versionchanged:: 2024.11
The ``cancellation_date`` attribute has been added and can also be used as an ordering key.
.. versionchanged:: 2025.1
The ``tax_code`` attribute has been added.
.. _order-position-resource:
@@ -193,14 +165,8 @@ country string Attendee countr
state string Attendee state (ISO 3166-2 code). Only supported in
AU, BR, CA, CN, MY, MX, and US, otherwise ``null``.
voucher integer Internal ID of the voucher used for this position (or ``null``)
voucher_budget_use money (string) Amount of money discounted by the voucher, corresponding
to how much of the ``budget`` of the voucher is consumed.
**Important:** Do not rely on this amount to be a useful
value if the position's price, product or voucher
are changed *after* the order was created. Can be ``null``.
tax_rate decimal (string) VAT rate applied for this position
tax_value money (string) VAT included in this position
tax_code string Codified reason for tax rate (or ``null``), see :ref:`rest-taxcodes`.
tax_rule integer The ID of the used tax rule (or ``null``)
secret string Secret code printed on the tickets for validation
addon_to integer Internal ID of the position this position is an add-on for (or ``null``)
@@ -216,20 +182,8 @@ checkins list of objects List of **succe
├ datetime datetime Time of check-in
├ type string Type of scan (defaults to ``entry``)
├ gate integer Internal ID of the gate. Can be ``null``.
├ device integer Internal ID of the device. Can be ``null``. **Deprecated**, since this ID is not otherwise used in the API and is therefore not very useful.
├ device_id integer Attribute ``device_id`` of the device. Can be ``null``.
├ device integer Internal ID of the device. Can be ``null``.
└ auto_checked_in boolean Indicates if this check-in been performed automatically by the system
print_logs list of objects List of print jobs recorded e.g. by the pretix apps
├ id integer Internal ID of the print job
├ successful boolean Whether the print job successfully resulted in a print.
This is not expected to be 100 % reliable information (since
printer feedback is never perfect) and there is no guarantee
that unsuccessful jobs will be logged.
├ device_id integer Attribute ``device_id`` of the device that recorded the print. Can be ``null``.
├ datetime datetime Time of printing
├ source string Source of print job, e.g. name of the app used.
├ type string Type of print (currently ``badge``, ``ticket``, ``certificate``, or ``other``)
└ info object Additional data with client-dependent structure.
downloads list of objects List of ticket download options
├ output string Ticket output provider (e.g. ``pdf``, ``passbook``)
└ url string Download URL
@@ -242,11 +196,6 @@ answers list of objects Answers to user
seat objects The assigned seat. Can be ``null``.
├ id integer Internal ID of the seat instance
├ name string Human-readable seat name
├ zone_name string Name of the zone the seat is in
├ row_name string Name/number of the row the seat is in
├ row_label string Additional label of the row (or ``null``)
├ seat_number string Number of the seat within the row
├ seat_label string Additional label of the seat (or ``null``)
└ seat_guid string Identifier of the seat within the seating plan
pdf_data object Data object required for ticket PDF generation. By default,
this field is missing. It will be added only if you add the
@@ -257,14 +206,6 @@ pdf_data object Data object req
The attributes ``blocked``, ``valid_from`` and ``valid_until`` have been added.
.. versionchanged:: 2024.9
The attribute ``print_logs`` has been added.
.. versionchanged:: 2025.1
The ``tax_code`` attribute has been added.
.. _order-payment-resource:
Order payment resource
@@ -373,7 +314,6 @@ List of all orders
"comment": "",
"custom_followup_at": null,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"valid_if_pending": false,
"invoice_address": {
@@ -412,11 +352,9 @@ List of all orders
"country": "DE",
"state": null,
"voucher": null,
"voucher_budget_use": null,
"tax_rate": "0.00",
"tax_value": "0.00",
"tax_rule": null,
"tax_code": null,
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null,
"subevent": null,
@@ -432,21 +370,10 @@ List of all orders
"type": "entry",
"gate": null,
"device": 2,
"device_id": 1,
"datetime": "2017-12-25T12:45:23Z",
"auto_checked_in": false
}
],
"print_logs": [
{
"id": 1,
"type": "badge",
"datetime": "2017-12-25T12:45:23Z",
"device_id": 1,
"source": "pretixSCAN",
"info": {}
}
],
"answers": [
{
"question": 12,
@@ -482,19 +409,17 @@ List of all orders
"provider": "banktransfer"
}
],
"refunds": [],
"cancellation_date": null
"refunds": []
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``datetime``, ``code``,
``last_modified``, ``status`` and ``cancellation_date``. Default: ``datetime``
``last_modified``, and ``status``. Default: ``datetime``
:query string code: Only return orders that match the given order code
:query string status: Only return orders in the given order status (see above)
:query string search: Only return orders matching a given search query (matching for names, email addresses, and company names)
:query string customer: Only show orders linked to the given customer.
:query integer item: Only return orders with a position that contains this item ID. *Warning:* Result will also include orders if they contain mixed items, and it will even return orders where the item is only contained in a canceled position.
:query integer variation: Only return orders with a position that contains this variation ID. *Warning:* Result will also include orders if they contain mixed items and variations, and it will even return orders where the variation is only contained in a canceled position.
:query boolean testmode: Only return orders with ``testmode`` set to ``true`` or ``false``
@@ -508,13 +433,10 @@ List of all orders
:query datetime modified_since: Only return orders that have changed since the given date. Be careful: We only
recommend using this in combination with ``testmode=false``, since test mode orders can vanish at any time and
you will not notice it using this method.
:query datetime created_since: Only return orders that have been created since the given date (inclusive).
:query datetime created_before: Only return orders that have been created before the given date (exclusive).
:query datetime created_since: Only return orders that have been created since the given date.
:query integer subevent: Only return orders with a position that contains this subevent ID. *Warning:* Result will also include orders if they contain mixed subevents, and it will even return orders where the subevent is only contained in a canceled position.
:query datetime subevent_after: Only return orders that contain a ticket for a subevent taking place after the given date. This is an exclusive after, and it considers the **end** of the subevent (or its start, if the end is not set).
:query datetime subevent_before: Only return orders that contain a ticket for a subevent taking place after the given date. This is an exclusive before, and it considers the **start** of the subevent.
:query string sales_channel: Only return orders with the given sales channel identifier (e.g. ``"web"``).
:query string payment_provider: Only return orders that contain a payment using the given payment provider. Note that this also searches for partial incomplete, or failed payments within the order and is not useful to get a sum of payment amounts without further processing.
:query string exclude: Exclude a field from the output, e.g. ``fees`` or ``positions.downloads``. Can be used as a performance optimization. Can be passed multiple times.
:query string include: Include only the given field in the output, e.g. ``fees`` or ``positions.downloads``. Can be used as a performance optimization. Can be passed multiple times. ``include`` is applied before ``exclude``, so ``exclude`` takes precedence.
:param organizer: The ``slug`` field of the organizer to fetch
@@ -610,10 +532,8 @@ Fetching individual orders
"fees": [],
"total": "23.00",
"comment": "",
"api_meta": {},
"custom_followup_at": null,
"checkin_attention": false,
"checkin_text": null,
"require_approval": false,
"valid_if_pending": false,
"invoice_address": {
@@ -652,11 +572,9 @@ Fetching individual orders
"country": "DE",
"state": null,
"voucher": null,
"voucher_budget_use": null,
"tax_rate": "0.00",
"tax_rule": null,
"tax_value": "0.00",
"tax_code": null,
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null,
"subevent": null,
@@ -672,22 +590,10 @@ Fetching individual orders
"type": "entry",
"gate": null,
"device": 2,
"device_id": 1,
"datetime": "2017-12-25T12:45:23Z",
"auto_checked_in": false
}
],
"print_logs": [
{
"id": 1,
"type": "badge",
"successful": true,
"datetime": "2017-12-25T12:45:23Z",
"device_id": 1,
"source": "pretixSCAN",
"info": {}
}
],
"answers": [
{
"question": 12,
@@ -723,8 +629,7 @@ Fetching individual orders
"provider": "banktransfer"
}
],
"refunds": [],
"cancellation_date": null
"refunds": []
}
:param organizer: The ``slug`` field of the organizer to fetch
@@ -799,22 +704,16 @@ Updating order fields
* ``checkin_attention``
* ``checkin_text``
* ``locale``
* ``comment``
* ``api_meta``
* ``custom_followup_at``
* ``invoice_address`` (you always need to supply the full object, or ``null`` to delete the current address)
* ``valid_if_pending``
* ``expires``
**Example request**:
.. sourcecode:: http
@@ -855,7 +754,7 @@ Generating new secrets
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/regenerate_secrets/
Triggers generation of new ``secret`` and ``web_secret`` attributes for both the order and all order positions.
Triggers generation of new ``secret`` attributes for both the order and all order positions.
**Example request**:
@@ -886,7 +785,7 @@ Generating new secrets
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/orderpositions/(id)/regenerate_secrets/
Triggers generation of a new ``secret`` and ``web_secret`` attribute for a single order position.
Triggers generation of a new ``secret`` attribute for a single order position.
**Example request**:
@@ -1020,7 +919,6 @@ Creating orders
* ``comment`` (optional)
* ``custom_followup_at`` (optional)
* ``checkin_attention`` (optional)
* ``checkin_text`` (optional)
* ``require_approval`` (optional)
* ``valid_if_pending`` (optional)
* ``invoice_address`` (optional)
@@ -1036,8 +934,8 @@ Creating orders
* ``internal_reference``
* ``vat_id``
* ``vat_id_validated`` (optional) If you need support for reverse charge (rarely the case), you need to check
yourself if the passed VAT ID is a valid EU VAT ID. In that case, set this to ``true``. Only valid VAT IDs will
trigger reverse charge taxation. Don't forget to set ``is_business`` as well!
yourself if the passed VAT ID is a valid EU VAT ID. In that case, set this to ``true``. Only valid VAT IDs will
trigger reverse charge taxation. Don't forget to set ``is_business`` as well!
* ``positions``
@@ -1621,11 +1519,9 @@ List of all order positions
},
"attendee_email": null,
"voucher": null,
"voucher_budget_use": null,
"tax_rate": "0.00",
"tax_rule": null,
"tax_value": "0.00",
"tax_code": null,
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"discount": null,
"pseudonymization_id": "MQLJvANO3B",
@@ -1641,22 +1537,10 @@ List of all order positions
"type": "entry",
"gate": null,
"device": 2,
"device_id": 1,
"datetime": "2017-12-25T12:45:23Z",
"auto_checked_in": false
}
],
"print_logs": [
{
"id": 1,
"type": "badge",
"successful": true,
"datetime": "2017-12-25T12:45:23Z",
"device_id": 1,
"source": "pretixSCAN",
"info": {}
}
],
"answers": [
{
"question": 12,
@@ -1682,7 +1566,6 @@ List of all order positions
``order__datetime,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 customer: Only show orders linked to the given customer.
: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.
@@ -1748,11 +1631,9 @@ Fetching individual positions
},
"attendee_email": null,
"voucher": null,
"voucher_budget_use": null,
"tax_rate": "0.00",
"tax_rule": null,
"tax_value": "0.00",
"tax_code": null,
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null,
"subevent": null,
@@ -1768,22 +1649,10 @@ Fetching individual positions
"type": "entry",
"gate": null,
"device": 2,
"device_id": 1,
"datetime": "2017-12-25T12:45:23Z",
"auto_checked_in": false
}
],
"print_logs": [
{
"id": 1,
"type": "badge",
"successful": true,
"datetime": "2017-12-25T12:45:23Z",
"device_id": 1,
"source": "pretixSCAN",
"info": {}
}
],
"answers": [
{
"question": 12,
@@ -1880,10 +1749,6 @@ Manipulating individual positions
The endpoints to manage blocks have been added.
.. versionchanged:: 2024.9
The API now supports logging ticket and badge prints.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/orderpositions/(id)/
Updates specific fields on an order position. Currently, only the following fields are supported:
@@ -2143,59 +2008,6 @@ Manipulating individual positions
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to update this order position.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/orderpositions/(id)/printlog/
Creates a print log, stating that this ticket has been printed.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/orderpositions/23442/printlog/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"datetime": "2024-09-19T13:37:00+02:00",
"source": "pretixPOS",
"type": "badge",
"info": {
"cashier": 1234
}
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/pdf
{
"id": 1234,
"device_id": null,
"datetime": "2024-09-19T13:37:00+02:00",
"source": "pretixPOS",
"type": "badge",
"info": {
"cashier": 1234
}
}
:param organizer: The ``slug`` field of the organizer to create a log for
:param event: The ``slug`` field of the event to create a log for
:param id: The ``id`` field of the order position to create a log for
:statuscode 201: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource
**or** downloads are not available for this order position at this time. The response content will
contain more details.
:statuscode 404: The requested order position or download provider does not exist.
:statuscode 409: The file is not yet ready and will now be prepared. Retry the request after waiting for a few
seconds.
Changing order contents
-----------------------

View File

@@ -44,8 +44,6 @@ identifier string An arbitrary st
ask_during_checkin boolean If ``true``, this question will not be asked while
buying the ticket, but will show up when redeeming
the ticket instead.
show_during_checkin boolean If ``true``, the answer to the question will be shown
during check-in (if the check-in client supports it).
hidden boolean If ``true``, the question will only be shown in the
backend.
print_on_invoice boolean If ``true``, the question will only be shown on
@@ -79,10 +77,6 @@ dependency_value string An old version
for one value. **Deprecated.**
===================================== ========================== =======================================================
.. versionchanged:: 2023.8
The ``show_during_checkin`` attribute has been added.
Endpoints
---------
@@ -121,7 +115,6 @@ Endpoints
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"show_during_checkin": false,
"hidden": false,
"print_on_invoice": false,
"valid_number_min": null,
@@ -201,7 +194,6 @@ Endpoints
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"show_during_checkin": false,
"hidden": false,
"print_on_invoice": false,
"valid_number_min": null,
@@ -265,7 +257,6 @@ Endpoints
"items": [1, 2],
"position": 1,
"ask_during_checkin": false,
"show_during_checkin": false,
"hidden": false,
"print_on_invoice": false,
"dependency_question": null,
@@ -302,7 +293,6 @@ Endpoints
"position": 1,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"show_during_checkin": false,
"hidden": false,
"print_on_invoice": false,
"dependency_question": null,
@@ -358,7 +348,7 @@ Endpoints
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/questions/1/ HTTP/1.1
PATCH /api/v1/organizers/bigevents/events/sampleconf/items/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
@@ -386,7 +376,6 @@ Endpoints
"position": 2,
"identifier": "WY3TP9SL",
"ask_during_checkin": false,
"show_during_checkin": false,
"hidden": false,
"print_on_invoice": false,
"dependency_question": null,
@@ -426,7 +415,7 @@ Endpoints
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the question to modify
:statuscode 200: no error
:statuscode 400: The question could not be modified due to invalid submitted data
:statuscode 400: The item could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
@@ -438,7 +427,7 @@ Endpoints
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/questions/1/ HTTP/1.1
DELETE /api/v1/organizers/bigevents/events/sampleconf/items/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
@@ -451,7 +440,7 @@ Endpoints
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the question to delete
:param id: The ``id`` field of the item to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.

View File

@@ -1,219 +0,0 @@
Sales channels
==============
Resource description
--------------------
The sales channel resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
identifier string Internal ID of the sales channel. For sales channel types
that allow only one instance, this is the same as ``type``.
For sales channel types that allow multiple instances, this
is always prefixed with ``type.``.
label multi-lingual string Human-readable name of the sales channel
type string Type of the sales channel. Only channels with type ``api``
can currently be created through the API.
position integer Position for sorting lists of sales channels
===================================== ========================== =======================================================
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/saleschannels/
Returns a list of all sales channels within a given organizer.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/saleschannels/ 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": "web",
"label": {
"en": "Online shop"
},
"type": "web",
"position": 0
}
]
}
: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)/saleschannels/(identifier)/
Returns information on one sales channel, identified by its identifier.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/saleschannels/web/ 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": "web",
"label": {
"en": "Online shop"
},
"type": "web",
"position": 0
}
:param organizer: The ``slug`` field of the organizer to fetch
:param identifier: The ``identifier`` field of the sales channel 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)/saleschannels/
Creates a sales channel
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/saleschannels/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"identifier": "api.custom",
"label": {
"en": "Custom integration"
},
"type": "api",
"position": 2
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"identifier": "api.custom",
"label": {
"en": "Custom integration"
},
"type": "api",
"position": 2
}
:param organizer: The ``slug`` field of the organizer to create a sales channel for
:statuscode 201: no error
:statuscode 400: The sales channel 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)/saleschannels/(identifier)/
Update a sales channel. 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`` and ``type`` fields.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/saleschannels/web/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"position": 5
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"identifier": "web",
"label": {
"en": "Online shop"
},
"type": "web",
"position": 5
}
:param organizer: The ``slug`` field of the organizer to modify
:param identifier: The ``identifier`` field of the sales channel to modify
:statuscode 200: no error
:statuscode 400: The sales channel 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)/saleschannels/(identifier)/
Delete a sales channel. You can not delete sales channels which have already been used or which are integral parts
of the system.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/saleschannels/api.custom/ 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 identifier: The ``identifier`` field of the sales channel 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 sales channel is currently in use.

View File

@@ -1,556 +0,0 @@
.. spelling:word-list:: checkin
Scheduled data exports
======================
pretix and it's plugins include a number of data exporters that allow you to bulk download various data from pretix in
different formats. You should read :ref:`rest-exporters` first to get an understanding of the basic mechanism.
Exports can be scheduled to be sent at specific times automatically, both on organizer level and event level.
Scheduled export resource
-------------------------
The scheduled export contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the schedule
owner string Email address of the user who created this schedule (read-only).
This address will always receive the export and the export
will only contain data that this user has permission
to access at the time of the export. **We consider this
field experimental, it's behaviour might change in the future.
Note that the email address of a user can change at any time.**
export_identifier string Identifier of the export to run, see :ref:`rest-exporters`
export_form_data object Input data for the export, format depends on the export,
see :ref:`rest-exporters` for more details.
locale string Language to run the export in
mail_additional_recipients string Email addresses to receive the export, comma-separated (or empty string)
mail_additional_recipients_cc string Email addresses to receive the export in copy, comma-separated (or empty string)
mail_additional_recipients_bcc string Email addresses to receive the exportin blind copy, comma-separated (or empty string)
mail_subject string Subject to use for the email (currently no variables supported)
mail_template string Text to use for the email (currently no variables supported)
schedule_rrule string Recurrence specification to determine the **days** this
schedule runs on in ``RRULE`` syntax following `RFC 5545`_
with some restrictions. Only one rule is allowed, only
one occurrence per day is allowed, and some features
are not supported (``BYMONTHDAY``, ``BYYEARDAY``,
``BYEASTER``, ``BYWEEKNO``).
schedule_rrule_time time Time of day to run this on on the specified days.
Will be interpreted as local time of the event for event-level
exports. For organizer-level exports, the timezone is given
in the field ``timezone``. The export will never run **before**
this time but it **may** run **later**.
timezone string Time zone to interpret the schedule in (only for organizer-level exports)
schedule_next_run datetime Next planned execution (read-only, computed by server)
error_counter integer Number of consecutive times this export failed (read-only).
After a number of failures (currently 5), the schedule no
longer is executed. Changing parameters resets the value.
===================================== ========================== =======================================================
Special notes on permissions
----------------------------
Permission handling for scheduled exports is more complex than for most other objects. The reason for this is that
there are two levels of access control involved here: First, you need permission to access or change the configuration
of the scheduled exports in the moment you are doing it. Second, you **continuously** need permission to access the
**data** that is exported as part of the schedule. For this reason, scheduled exports always need one user account
to be their **owner**.
Therefore, scheduled exports **must** be created by an API client using :ref:`OAuth authentication <rest-oauth>`.
It is impossible to create a scheduled export using token authentication. After the export is created, it can also be
modified using token authentication.
A user or token with the "can change settings" permission for a given organizer or event can see and change
**all** scheduled exports created for the respective organizer or event, regardless of who created them.
A user without this permission can only see **their own** scheduled exports.
A token without this permission can not see scheduled exports as all.
Endpoints for event exports
---------------------------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/scheduled_exports/
Returns a list of all scheduled exports the client has access to.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/scheduled_exports/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"owner": "john@example.com",
"export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
"locale": "en",
"mail_additional_recipients": "mary@example.org",
"mail_additional_recipients_cc": "",
"mail_additional_recipients_bcc": "",
"mail_subject": "Order list",
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
"schedule_rrule_time": "04:00:00",
"schedule_next_run": "2023-10-26T02:00:00Z",
"error_counter": 0
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``id``, ``export_identifier``, and ``schedule_next_run``.
Default: ``id``
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/scheduled_exports/(id)/
Returns information on one scheduled export, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/scheduled_exports/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"owner": "john@example.com",
"export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
"locale": "en",
"mail_additional_recipients": "mary@example.org",
"mail_additional_recipients_cc": "",
"mail_additional_recipients_bcc": "",
"mail_subject": "Order list",
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
"schedule_rrule_time": "04:00:00",
"schedule_next_run": "2023-10-26T02:00:00Z",
"error_counter": 0
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the scheduled export to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/scheduled_exports/
Schedule a new export.
.. note:: See above for special notes on permissions.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/scheduled_exports/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
"locale": "en",
"mail_additional_recipients": "mary@example.org",
"mail_additional_recipients_cc": "",
"mail_additional_recipients_bcc": "",
"mail_subject": "Order list",
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
"schedule_rrule_time": "04:00:00"
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"owner": "john@example.com",
"export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
"locale": "en",
"mail_additional_recipients": "mary@example.org",
"mail_additional_recipients_cc": "",
"mail_additional_recipients_bcc": "",
"mail_subject": "Order list",
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
"schedule_rrule_time": "04:00:00",
"schedule_next_run": "2023-10-26T02:00:00Z",
"error_counter": 0
}
:param organizer: The ``slug`` field of the organizer of the event to create an item for
:param event: The ``slug`` field of the event to create an item for
:statuscode 201: no error
:statuscode 400: The item could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/scheduled_exports/(id)/
Update a scheduled export. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/scheduled_exports/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"export_form_data": {"_format": "xlsx", "date_range": "week_this"},
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"owner": "john@example.com",
"export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "week_this"},
"locale": "en",
"mail_additional_recipients": "mary@example.org",
"mail_additional_recipients_cc": "",
"mail_additional_recipients_bcc": "",
"mail_subject": "Order list",
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
"schedule_rrule_time": "04:00:00",
"schedule_next_run": "2023-10-26T02:00:00Z",
"error_counter": 0
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the export to modify
:statuscode 200: no error
:statuscode 400: The export could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/scheduled_exports/(id)/
Delete a scheduled export.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/scheduled_exports/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the export to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.
Endpoints for organizer exports
-------------------------------
.. http:get:: /api/v1/organizers/(organizer)/scheduled_exports/
Returns a list of all scheduled exports the client has access to.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/scheduled_exports/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"owner": "john@example.com",
"export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
"locale": "en",
"mail_additional_recipients": "mary@example.org",
"mail_additional_recipients_cc": "",
"mail_additional_recipients_bcc": "",
"mail_subject": "Order list",
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
"schedule_rrule_time": "04:00:00",
"schedule_next_run": "2023-10-26T02:00:00Z",
"timezone": "Europe/Berlin",
"error_counter": 0
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``id``, ``export_identifier``, and ``schedule_next_run``.
Default: ``id``
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/scheduled_exports/(id)/
Returns information on one scheduled export, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/scheduled_exports/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"owner": "john@example.com",
"export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
"locale": "en",
"mail_additional_recipients": "mary@example.org",
"mail_additional_recipients_cc": "",
"mail_additional_recipients_bcc": "",
"mail_subject": "Order list",
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
"schedule_rrule_time": "04:00:00",
"schedule_next_run": "2023-10-26T02:00:00Z",
"timezone": "Europe/Berlin",
"error_counter": 0
}
:param organizer: The ``slug`` field of the organizer to fetch
:param id: The ``id`` field of the scheduled export to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/scheduled_exports/
Schedule a new export.
.. note:: See above for special notes on permissions.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/scheduled_exports/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
"locale": "en",
"mail_additional_recipients": "mary@example.org",
"mail_additional_recipients_cc": "",
"mail_additional_recipients_bcc": "",
"mail_subject": "Order list",
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
"schedule_rrule_time": "04:00:00",
"timezone": "Europe/Berlin"
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"owner": "john@example.com",
"export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "week_previous"},
"locale": "en",
"mail_additional_recipients": "mary@example.org",
"mail_additional_recipients_cc": "",
"mail_additional_recipients_bcc": "",
"mail_subject": "Order list",
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
"schedule_rrule_time": "04:00:00",
"schedule_next_run": "2023-10-26T02:00:00Z",
"timezone": "Europe/Berlin",
"error_counter": 0
}
:param organizer: The ``slug`` field of the organizer of the event to create an item for
:statuscode 201: no error
:statuscode 400: The item could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/scheduled_exports/(id)/
Update a scheduled export. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/scheduled_exports/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"export_form_data": {"_format": "xlsx", "date_range": "week_this"},
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"owner": "john@example.com",
"export_identifier": "orderlist",
"export_form_data": {"_format": "xlsx", "date_range": "week_this"},
"locale": "en",
"mail_additional_recipients": "mary@example.org",
"mail_additional_recipients_cc": "",
"mail_additional_recipients_bcc": "",
"mail_subject": "Order list",
"mail_template": "Here is last week's order list\n\nCheers\nJohn",
"schedule_rrule": "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;BYDAY=TU,WE,TH",
"schedule_rrule_time": "04:00:00",
"schedule_next_run": "2023-10-26T02:00:00Z",
"timezone": "Europe/Berlin",
"error_counter": 0
}
:param organizer: The ``slug`` field of the organizer to modify
:param id: The ``id`` field of the export to modify
:statuscode 200: no error
:statuscode 400: The export could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource.
.. http:delete:: /api/v1/organizers/(organizer)/scheduled_exports/(id)/
Delete a scheduled export.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/scheduled_exports/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param id: The ``id`` field of the export to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to delete this resource.
.. _RFC 5545: https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.5.3

View File

@@ -1,373 +0,0 @@
.. _`rest-seats`:
Seats
=====
The seat resource represents the seats in a seating plan in a specific event or subevent.
Resource description
--------------------
The seat resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of this seat
subevent integer Internal ID of the subevent this seat belongs to
zone_name string Name of the zone the seat is in
row_name string Name/number of the row the seat is in
row_label string Additional label of the row (or ``null``)
seat_number string Number of the seat within the row
seat_label string Additional label of the seat (or ``null``)
seat_guid string Identifier of the seat within the seating plan
product integer Internal ID of the product that is mapped to this seat
blocked boolean Whether this seat is blocked manually.
orderposition integer / object Internal ID of an order position reserving this seat.
cartposition integer / object Internal ID of a cart position reserving this seat.
voucher integer / object Internal ID of a voucher reserving this seat.
===================================== ========================== =======================================================
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/seats/
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/subevents/(subevent_id)/seats/
Returns a list of all seats in the specified event or subevent. Depending on whether the event has subevents, the
according endpoint has to be used.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/seats/ HTTP/1.1
Host: pretix.eu
Accept: application/json
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 500,
"next": "https://pretix.eu/api/v1/organizers/bigevents/events/sampleconf/seats/?page=2",
"previous": null,
"results": [
{
"id": 1633,
"subevent": null,
"zone_name": "Ground floor",
"row_name": "1",
"row_label": null,
"seat_number": "1",
"seat_label": null,
"seat_guid": "b9746230-6f31-4f41-bbc9-d6b60bdb3342",
"product": 104,
"blocked": false,
"orderposition": null,
"cartposition": null,
"voucher": 51
},
{
"id": 1634,
"subevent": null,
"zone_name": "Ground floor",
"row_name": "1",
"row_label": null,
"seat_number": "2",
"seat_label": null,
"seat_guid": "1d29fe20-8e1e-4984-b0ee-2773b0d07e07",
"product": 104,
"blocked": true,
"orderposition": 4321,
"cartposition": null,
"voucher": null
},
// ...
]
}
:query integer page: The page number in case of a multi-page result set, default is 1.
:query string zone_name: Only show seats with the given zone_name.
:query string row_name: Only show seats with the given row_name.
:query string row_label: Only show seats with the given row_label.
:query string seat_number: Only show seats with the given seat_number.
:query string seat_label: Only show seats with the given seat_label.
:query string seat_guid: Only show seats with the given seat_guid.
:query boolean blocked: Only show seats with the given blocked status.
:query boolean is_available: Only show seats that are (not) currently available.
:query string expand: If you pass ``"orderposition"``, ``"cartposition"``, or ``"voucher"``, the respective field will be
shown as a nested value instead of just an ID. This requires permission to access that object.
The nested objects are identical to the respective resources, except that order positions
will have an attribute of the format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make
matching easier, and won't include the `seat` attribute, as that would be redundant.
The parameter can be given multiple times.
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param subevent_id: The ``id`` field of the subevent 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.
:statuscode 404: Endpoint without subevent id was used for event with subevents, or vice versa.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/seats/(id)/
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/subevents/(subevent_id)/seats/(id)/
Returns information on one seat, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/seats/1634/?expand=orderposition HTTP/1.1
Host: pretix.eu
Accept: application/json
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1634,
"subevent": null,
"zone_name": "Ground floor",
"row_name": "1",
"row_label": null,
"seat_number": "2",
"seat_label": null,
"seat_guid": "1d29fe20-8e1e-4984-b0ee-2773b0d07e07",
"product": 104,
"blocked": true,
"orderposition": {
"id": 134,
"order": {
"code": "U0HW7",
"event": "sampleconf"
},
"positionid": 1,
"item": 104,
"variation": 59,
"price": "60.00",
"attendee_name": "",
"attendee_name_parts": {
"_scheme": "given_family"
},
"company": null,
"street": null,
"zipcode": null,
"city": null,
"country": null,
"state": null,
"discount": null,
"attendee_email": null,
"voucher": null,
"tax_rate": "0.00",
"tax_value": "0.00",
"secret": "4rfgp263jduratnsvwvy6cc6r6wnptbj",
"addon_to": null,
"subevent": null,
"checkins": [],
"downloads": [],
"answers": [],
"tax_rule": null,
"pseudonymization_id": "ZSNYSG3URZ",
"canceled": false,
"valid_from": null,
"valid_until": null,
"blocked": null,
"voucher_budget_use": null
},
"cartposition": null,
"voucher": null
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param subevent_id: The ``id`` field of the subevent to fetch
:param id: The ``id`` field of the seat to fetch
:query string expand: If you pass ``"orderposition"``, ``"cartposition"``, or ``"voucher"``, the respective field will be
shown as a nested value instead of just an ID. This requires permission to access that object.
The nested objects are identical to the respective resources, except that order positions
will have an attribute of the format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make
matching easier, and won't include the `seat` attribute, as that would be redundant.
The parameter can be given multiple times.
: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.
:statuscode 404: Seat does not exist; or the endpoint without subevent id was used for event with subevents, or vice versa.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/seats/(id)/
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/subevents/(id)/seats/(id)/
Update a seat.
You can only change the ``blocked`` field.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/seats/1636/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"blocked": true
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1636,
"subevent": null,
"zone_name": "Ground floor",
"row_name": "1",
"row_label": null,
"seat_number": "4",
"seat_label": null,
"seat_guid": "6c0e29e5-05d6-421f-99f3-afd01478ecad",
"product": 104,
"blocked": true,
"orderposition": null,
"cartposition": null,
"voucher": null
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param subevent_id: The ``id`` field of the subevent to modify
:param id: The ``id`` field of the seat to modify
:statuscode 200: no error
:statuscode 400: The seat could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer or event does not exist **or** you have no permission to change this resource.
:statuscode 404: Seat does not exist; or the endpoint without subevent id was used for event with subevents, or vice versa.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/seats/bulk_block/
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/subevents/(id)/seats/bulk_block/
Set the ``blocked`` attribute to ``true`` for a large number of seats at once.
You can pass either a list of ``id`` values or a list of ``seat_guid`` values.
You can pass up to 10,000 seats in one request.
The endpoint will return an error if you pass a seat ID that does not exist.
However, it will not return an error if one of the passed seats is already blocked or sold.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/seats/bulk_block/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"ids": [12, 45, 56]
}
or
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/seats/bulk_block/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"seat_guids": ["6c0e29e5-05d6-421f-99f3-afd01478ecad", "c2899340-e2e7-4d05-8100-000a4b6d7cf4"]
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param subevent_id: The ``id`` field of the subevent to modify
:statuscode 200: no error
:statuscode 400: The seat could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer or event does not exist **or** you have no permission to change this resource.
:statuscode 404: Seat does not exist; or the endpoint without subevent id was used for event with subevents, or vice versa.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/seats/bulk_unblock/
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/subevents/(id)/seats/bulk_unblock/
Set the ``blocked`` attribute to ``false`` for a large number of seats at once.
You can pass either a list of ``id`` values or a list of ``seat_guid`` values.
You can pass up to 10,000 seats in one request.
The endpoint will return an error if you pass a seat ID that does not exist.
However, it will not return an error if one of the passed seat is already unblocked or is sold.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/seats/bulk_unblock/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"ids": [12, 45, 56]
}
or
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/seats/bulk_unblock/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"seat_guids": ["6c0e29e5-05d6-421f-99f3-afd01478ecad", "c2899340-e2e7-4d05-8100-000a4b6d7cf4"]
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param subevent_id: The ``id`` field of the subevent to modify
:statuscode 200: no error
:statuscode 400: The seat could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer or event does not exist **or** you have no permission to change this resource.
:statuscode 404: Seat does not exist; or the endpoint without subevent id was used for event with subevents, or vice versa.

View File

@@ -1,8 +1,6 @@
Scheduled email rules
=====================
This feature requires the bundled ``pretix.plugins.sendmail`` plugin to be active for the event in order to work properly.
Resource description
--------------------
@@ -50,7 +48,6 @@ send_to string Can be ``"order
or ``"both"``.
date. Otherwise it is relative to the event start date.
===================================== ========================== =======================================================
.. versionchanged:: 2023.7
The ``include_pending`` field has been deprecated.

View File

@@ -136,7 +136,6 @@ Endpoints
}
:query page: The page number in case of a multi-page result set, default is 1
:query is_public: If set to ``true``/``false``, only subevents with a matching value of ``is_public`` are returned.
:query active: If set to ``true``/``false``, only events with a matching value of ``active`` are returned.
:query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned.
:query is_past: If set to ``true`` (``false``), only events that are over are (not) returned.
@@ -468,7 +467,6 @@ Endpoints
}
:query page: The page number in case of a multi-page result set, default is 1
:query is_public: If set to ``true``/``false``, only subevents with a matching value of ``is_public`` are returned.
:query active: If set to ``true``/``false``, only events with a matching value of ``active`` are returned.
:query event__live: If set to ``true``/``false``, only events with a matching value of ``live`` on the parent event are returned.
:query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned.

View File

@@ -1,8 +1,3 @@
.. spelling:word-list::
EN16931
DSFinV-K
.. _rest-taxrules:
Tax rules
@@ -23,12 +18,10 @@ id integer Internal ID of
name multi-lingual string The tax rules' name
internal_name string An optional name that is only used in the backend
rate decimal (string) Tax rate in percent
code string Codified reason for tax rate (or ``null``), see :ref:`rest-taxcodes`.
price_includes_tax boolean If ``true`` (default), tax is assumed to be included in
the specified product price
eu_reverse_charge boolean **DEPRECATED**. If ``true``, EU reverse charge rules
are applied. Will be ignored if custom rules are set.
Use custom rules instead.
eu_reverse_charge boolean If ``true``, EU reverse charge rules are applied. Will
be ignored if custom rules are set.
home_country string Merchant country (required for reverse charge), can be
``null`` or empty string
keep_gross_if_rate_changes boolean If ``true``, changes of the tax rate based on custom
@@ -48,42 +41,6 @@ custom_rules object Dynamic rules s
The ``custom_rules`` attribute has been added.
.. versionchanged:: 2023.8
The ``code`` attribute has been added.
.. _rest-taxcodes:
Tax codes
---------
For integration with external systems, such as electronic invoicing or bookkeeping systems, the tax rate itself is often
not sufficient information. For example, there could be many different reasons why a sale has a tax rate of 0 %, but the
external handling of the transaction depends on which reason applies. Therefore, pretix allows to supply a codified
reason that allows us to understand what the specific legal situation is. These tax codes are modeled after a combination
of the code lists from the European standard EN16931 and the German standard DSFinV-K.
The following codes are supported:
- ``S/standard`` -- Standard VAT rate in the merchant country
- ``S/reduced`` -- Reduced VAT rate in the merchant country
- ``S/averaged`` -- Averaged VAT rate in the merchant country (known use case: agricultural businesses in Germany)
- ``AE`` -- Reverse charge
- ``O`` -- Services outside of scope of tax
- ``E`` -- Exempt from tax (no reason given)
- ``E/<reason>`` -- Exempt from tax, where ``<reason>`` is one of the codes listed in the `VATEX code list`_ version 5.0.
- ``Z`` -- Zero-rated goods
- ``G`` -- Free export item, VAT not charged
- ``K`` -- VAT exempt for EEA intra-community supply of goods and services
- ``L`` -- Canary Islands general indirect tax
- ``M`` -- Tax for production, services and importation in Ceuta and Melilla
- ``B`` -- Transferred (VAT), only in Italy
The code set in the ``code`` attribute of the tax rule is used by default. When ``eu_reverse_charge`` is active, the
code is replaced by ``AE`` for reverse charge sales and by ``O`` for non-EU sales. When configuring custom rules, you
should actively set a ``"code"`` key on each rule. Only for ``"action": "reverse"`` we automatically apply the code
``AE``, in all other cases the default ``code`` of the tax rule is selected.
Endpoints
---------
@@ -116,7 +73,6 @@ Endpoints
"id": 1,
"name": {"en": "VAT"},
"internal_name": "VAT",
"code": "S/standard",
"rate": "19.00",
"price_includes_tax": true,
"eu_reverse_charge": false,
@@ -158,7 +114,6 @@ Endpoints
"id": 1,
"name": {"en": "VAT"},
"internal_name": "VAT",
"code": "S/standard",
"rate": "19.00",
"price_includes_tax": true,
"eu_reverse_charge": false,
@@ -208,7 +163,6 @@ Endpoints
"id": 1,
"name": {"en": "VAT"},
"internal_name": "VAT",
"code": "S/standard",
"rate": "19.00",
"price_includes_tax": true,
"eu_reverse_charge": false,
@@ -257,7 +211,6 @@ Endpoints
"id": 1,
"name": {"en": "VAT"},
"internal_name": "VAT",
"code": "S/standard",
"rate": "20.00",
"price_includes_tax": true,
"eu_reverse_charge": false,
@@ -304,4 +257,3 @@ Endpoints
:statuscode 403: The requested organizer/event/rule does not exist **or** you have no permission to change it **or** this tax rule cannot be deleted since it is currently in use.
.. _here: https://github.com/pretix/pretix/blob/master/src/pretix/static/schema/tax-rules-custom.schema.json
.. _VATEX code list: https://ec.europa.eu/digital-building-blocks/sites/display/DIGITAL/Registry+of+supporting+artefacts+to+implement+EN16931#RegistryofsupportingartefactstoimplementEN16931-Codelists

View File

@@ -22,8 +22,6 @@ id integer Internal ID of
name string Team name
all_events boolean Whether this team has access to all events
limit_events list List of event slugs this team has access to
require_2fa boolean Whether members of this team are required to use
two-factor authentication
can_create_events boolean
can_change_teams boolean
can_change_organizer_settings boolean
@@ -124,7 +122,6 @@ Team endpoints
"name": "Admin team",
"all_events": true,
"limit_events": [],
"require_2fa": true,
"can_create_events": true,
...
}
@@ -162,7 +159,6 @@ Team endpoints
"name": "Admin team",
"all_events": true,
"limit_events": [],
"require_2fa": true,
"can_create_events": true,
...
}
@@ -190,7 +186,6 @@ Team endpoints
"name": "Admin team",
"all_events": true,
"limit_events": [],
"require_2fa": true,
"can_create_events": true,
...
}
@@ -208,7 +203,6 @@ Team endpoints
"name": "Admin team",
"all_events": true,
"limit_events": [],
"require_2fa": true,
"can_create_events": true,
...
}
@@ -252,7 +246,6 @@ Team endpoints
"name": "Admin team",
"all_events": true,
"limit_events": [],
"require_2fa": true,
"can_create_events": true,
...
}

View File

@@ -41,7 +41,6 @@ The following values for ``action_types`` are valid with pretix core:
* ``pretix.event.order.modified``
* ``pretix.event.order.contact.changed``
* ``pretix.event.order.changed.*``
* ``pretix.event.order.deleted`` (can only occur for test mode orders)
* ``pretix.event.order.refund.created``
* ``pretix.event.order.refund.created.externally``
* ``pretix.event.order.refund.requested``
@@ -116,7 +115,6 @@ Endpoints
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query boolean enabled: Only show webhooks that are or are not enabled
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure

View File

@@ -17,7 +17,6 @@ First, you need to declare that you are using non-essential cookies by respondin
signal:
.. automodule:: pretix.presale.signals
:no-index:
:members: register_cookie_providers
You are expected to return a list of ``CookieProvider`` objects instantiated from the following class:

View File

@@ -11,10 +11,9 @@ Core
----
.. automodule:: pretix.base.signals
:members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types, notification,
item_copy_data, register_sales_channel_types, register_global_settings, quota_availability, global_email_filter,
register_ticket_secret_generators, gift_card_transaction_display,
register_text_placeholders, register_mail_placeholders, device_info_updated
:members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types,
item_copy_data, register_sales_channels, register_global_settings, quota_availability, global_email_filter,
register_ticket_secret_generators, gift_card_transaction_display
Order events
""""""""""""
@@ -22,14 +21,12 @@ Order events
There are multiple signals that will be sent out in the ordering cycle:
.. automodule:: pretix.base.signals
:no-index:
:members: validate_cart, validate_cart_addons, validate_order, order_valid_if_pending, order_fee_calculation, order_paid, order_placed, order_canceled, order_reactivated, order_expired, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download, order_split, order_gracefully_delete, invoice_line_text
Check-ins
"""""""""
.. automodule:: pretix.base.signals
:no-index:
:members: checkin_created
@@ -37,25 +34,22 @@ Frontend
--------
.. automodule:: pretix.presale.signals
:members: html_head, html_footer, footer_link, global_footer_link, front_page_top, front_page_bottom, front_page_bottom_widget, fee_calculation_for_cart, contact_form_fields, question_form_fields, contact_form_fields_overrides, question_form_fields_overrides, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, render_seating_plan, checkout_flow_steps, position_info, position_info_top, item_description, global_html_head, global_html_footer, global_html_page_header, seatingframe_html_head
:members: html_head, html_footer, footer_link, global_footer_link, front_page_top, front_page_bottom, front_page_bottom_widget, fee_calculation_for_cart, contact_form_fields, question_form_fields, contact_form_fields_overrides, question_form_fields_overrides, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, sass_preamble, sass_postamble, render_seating_plan, checkout_flow_steps, position_info, position_info_top, item_description, global_html_head, global_html_footer, global_html_page_header
.. automodule:: pretix.presale.signals
:no-index:
:members: order_info, order_info_top, order_meta_from_request, order_api_meta_from_request
:members: order_info, order_info_top, order_meta_from_request
Request flow
""""""""""""
.. automodule:: pretix.presale.signals
:no-index:
:members: process_request, process_response
Vouchers
""""""""
.. automodule:: pretix.presale.signals
:no-index:
:members: voucher_redeem_info
Backend
@@ -67,28 +61,24 @@ Backend
item_formsets, order_search_filter_q, order_search_forms
.. automodule:: pretix.base.signals
:no-index:
:members: logentry_display, logentry_object_link, requiredaction_display, timeline_events, orderposition_blocked_display, customer_created, customer_signed_in
Vouchers
""""""""
.. automodule:: pretix.control.signals
:no-index:
:members: item_forms, voucher_form_class, voucher_form_html, voucher_form_validation
Dashboards
""""""""""
.. automodule:: pretix.control.signals
:no-index:
:members: event_dashboard_widgets, user_dashboard_widgets, event_dashboard_top
Ticket designs
""""""""""""""
.. automodule:: pretix.base.signals
:no-index:
:members: layout_text_variables, layout_image_variables
.. automodule:: pretix.plugins.ticketoutputpdf.signals
@@ -98,9 +88,4 @@ API
---
.. automodule:: pretix.base.signals
:no-index:
:members: validate_event_settings, api_event_settings_fields
.. automodule:: pretix.api.signals
:no-index:
:members: register_device_security_profile

View File

@@ -3,12 +3,11 @@
.. _`importcol`:
Extending the import process
============================
Extending the order import process
==================================
It's possible through the backend to import objects into pretix, for example orders from a legacy ticketing system. If
your plugin defines additional data structures around those objects, it might be useful to make it possible to import
them as well.
It's possible through the backend to import orders into pretix, for example from a legacy ticketing system. If your
plugins defines additional data structures around orders, it might be useful to make it possible to import them as well.
Import process
--------------
@@ -41,7 +40,7 @@ Column registration
The import API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available import columns. Your plugin
should listen for this signal and return the subclass of ``pretix.base.modelimport.ImportColumn``
should listen for this signal and return the subclass of ``pretix.base.orderimport.ImportColumn``
that we'll provide in this plugin:
.. sourcecode:: python
@@ -57,17 +56,10 @@ that we'll provide in this plugin:
EmailColumn(sender),
]
Similar signals exist for other objects:
.. automodule:: pretix.base.signals
:no-index:
:members: voucher_import_columns
The column class API
--------------------
.. class:: pretix.base.modelimport.ImportColumn
.. class:: pretix.base.orderimport.ImportColumn
The central object of each import extension is the subclass of ``ImportColumn``.

View File

@@ -1,11 +1,10 @@
.. highlight:: python
:linenothreshold: 5
Writing a template placeholder plugin
=====================================
Writing an e-mail placeholder plugin
====================================
A template placeholder is a dynamic value that pretix users can use in their email templates and in other
configurable texts.
An email placeholder is a dynamic value that pretix users can use in their email templates.
Please read :ref:`Creating a plugin <pluginsetup>` first, if you haven't already.
@@ -13,31 +12,31 @@ Placeholder registration
------------------------
The placeholder API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available placeholders. Your plugin
should listen for this signal and return an instance of a subclass of ``pretix.base.services.placeholders.BaseTextPlaceholder``:
does use a signal to get a list of all available email placeholders. Your plugin
should listen for this signal and return an instance of a subclass of ``pretix.base.email.BaseMailTextPlaceholder``:
.. code-block:: python
from django.dispatch import receiver
from pretix.base.signals import register_text_placeholders
from pretix.base.signals import register_mail_placeholders
@receiver(register_text_placeholders, dispatch_uid="placeholder_custom")
def register_placeholder_renderers(sender, **kwargs):
from .placeholders import MyPlaceholderClass
@receiver(register_mail_placeholders, dispatch_uid="placeholder_custom")
def register_mail_renderers(sender, **kwargs):
from .email import MyPlaceholderClass
return MyPlaceholder()
Context mechanism
-----------------
Templates are used in different "contexts" within pretix. For example, many emails are rendered from
templates in the context of an order, but some are not, such as the notification of a waiting list voucher.
Emails are sent in different "contexts" within pretix. For example, many emails are sent in the
the context of an order, but some are not, such as the notification of a waiting list voucher.
Not all placeholders make sense everywhere, and placeholders usually depend on some parameters
Not all placeholders make sense in every email, and placeholders usually depend some parameters
themselves, such as the ``Order`` object. Therefore, placeholders are expected to explicitly declare
what values they depend on and they will only be available in a context where all those dependencies are
what values they depend on and they will only be available in an email if all those dependencies are
met. Currently, placeholders can depend on the following context parameters:
* ``event``
@@ -52,7 +51,7 @@ There are a few more that are only to be used internally but not by plugins.
The placeholder class
---------------------
.. class:: pretix.base.services.placeholders.BaseTextPlaceholder
.. class:: pretix.base.email.BaseMailTextPlaceholder
.. autoattribute:: identifier
@@ -78,18 +77,7 @@ functions:
.. code-block:: python
placeholder = SimpleFunctionalTextPlaceholder(
placeholder = SimpleFunctionalMailTextPlaceholder(
'code', ['order'], lambda order: order.code, sample='F8VVL'
)
Signals
-------
.. automodule:: pretix.base.signals
:no-index:
:members: register_text_placeholders
.. automodule:: pretix.base.signals
:no-index:
:members: register_mail_placeholders

View File

@@ -121,7 +121,6 @@ This will automatically make pretix discover this plugin as soon as it is instal
through ``pip``. During development, you can just run ``python setup.py develop`` inside
your plugin source directory to make it discoverable.
.. _`signals`:
Signals
-------
@@ -154,25 +153,6 @@ in the ``installed`` method:
Note that ``installed`` will *not* be called if the plugin is indirectly activated for an event
because the event is created with settings copied from another event.
.. _`registries`:
Registries
----------
Many signals in pretix are used so that plugins can "register" a class, e.g. a payment provider or a
ticket renderer.
However, for some of them (types of :ref:`Log Entries <logging>`) we use a different method to keep track of them:
In a ``Registry``, classes are collected at application startup, along with a unique key (in case
of LogEntryType, the ``action_type``) as well as which plugin registered them.
To register a class, you can use one of several decorators provided by the Registry object:
.. autoclass:: pretix.base.logentrytypes.LogEntryTypeRegistry
:members: register, new, new_from_dict
All files in which classes are registered need to be imported in the ``AppConfig.ready`` as explained
in `Signals <signals>`_ above.
Views
-----

View File

@@ -17,7 +17,7 @@ The project pretix is split into several components. The main components are:
create and manage their events, items, orders and tickets.
**presale**
This is the ticket shop itself, containing all of the parts visible to the
This is the ticket-shop itself, containing all of the parts visible to the
end user. Also called "frontend" in parts of this documentation.
**api**

View File

@@ -19,4 +19,3 @@ Contents:
permissions
logging
locking
timemachine

View File

@@ -20,8 +20,7 @@ To actually log an action, you can just call the ``log_action`` method on your o
.. code-block:: python
order.log_action('pretix.event.order.comment', user=user,
data={"new_comment": "Hello, world."})
order.log_action('pretix.event.order.canceled', user=user, data={})
The positional ``action`` argument should represent the type of action and should be globally unique, we
recommend to prefix it with your package name, e.g. ``paypal.payment.rejected``. The ``user`` argument is
@@ -73,101 +72,24 @@ following ready-to-include template::
{% include "pretixcontrol/includes/logs.html" with obj=order %}
We now need a way to translate the action codes like ``pretix.event.changed`` into human-readable
strings. The :py:attr:`pretix.base.logentrytypes.log_entry_types` :ref:`registry <registries>` allows you to do so. A simple
strings. The :py:attr:`pretix.base.signals.logentry_display` signals allows you to do so. A simple
implementation could look like:
.. code-block:: python
from django.utils.translation import gettext as _
from pretix.base.logentrytypes import log_entry_types
@log_entry_types.new_from_dict({
'pretix.event.order.comment': _('The order\'s internal comment has been updated to: {new_comment}'),
'pretix.event.order.paid': _('The order has been marked as paid.'),
# ...
})
class CoreOrderLogEntryType(OrderLogEntryType):
pass
Please note that you always need to define your own inherited ``LogEntryType`` class in your plugin. If you would just
register an instance of a ``LogEntryType`` class defined in pretix core, it cannot be automatically detected as belonging
to your plugin, leading to confusing user interface situations.
Customizing log entry display
"""""""""""""""""""""""""""""
The base ``LogEntryType`` classes allow for varying degree of customization in their descendants.
If you want to add another log message for an existing core object (e.g. an :class:`Order <pretix.base.models.Order>`,
:class:`Item <pretix.base.models.Item>`, or :class:`Voucher <pretix.base.models.Voucher>`), you can inherit
from its predefined :class:`LogEntryType <pretix.base.logentrytypes.LogEntryType>`, e.g.
:class:`OrderLogEntryType <pretix.base.logentrytypes.OrderLogEntryType>`, and just specify a new plaintext string.
You can use format strings to insert information from the LogEntry's `data` object as shown in the section above.
If you define a new model object in your plugin, you should make sure proper object links in the user interface are
displayed for it. If your model object belongs logically to a pretix :class:`Event <pretix.base.models.Event>`, you can inherit from :class:`EventLogEntryType <pretix.base.logentrytypes.EventLogEntryType>`,
and set the ``object_link_*`` fields accordingly. ``object_link_viewname`` refers to a django url name, which needs to
accept the arguments `organizer` and `event`, containing the respective slugs, and additional arguments provided by
``object_link_args``. The default implementation of ``object_link_args`` will return an argument named by
````object_link_argname``, with a value of ``content_object.pk`` (the primary key of the model object).
If you want to customize the name displayed for the object (instead of the result of calling ``str()`` on it),
overwrite ``object_link_display_name``.
.. code-block:: python
class ItemLogEntryType(EventLogEntryType):
object_link_wrapper = _('Product {val}')
# link will be generated as reverse('control:event.item', {'organizer': ..., 'event': ..., 'item': item.pk})
object_link_viewname = 'control:event.item'
object_link_argname = 'item'
.. code-block:: python
class OrderLogEntryType(EventLogEntryType):
object_link_wrapper = _('Order {val}')
# link will be generated as reverse('control:event.order', {'organizer': ..., 'event': ..., 'code': order.code})
object_link_viewname = 'control:event.order'
def object_link_args(self, order):
return {'code': order.code}
def object_link_display_name(self, order):
return order.code
To show more sophisticated message strings, e.g. varying the message depending on information from the :class:`LogEntry <pretix.base.models.log.LogEntry>`'s
`data` object, override the `display` method:
.. code-block:: python
@log_entry_types.new()
class PaypalEventLogEntryType(EventLogEntryType):
action_type = 'pretix.plugins.paypal.event'
def display(self, logentry):
event_type = logentry.parsed_data.get('event_type')
text = {
'PAYMENT.SALE.COMPLETED': _('Payment completed.'),
'PAYMENT.SALE.DENIED': _('Payment denied.'),
# ...
}.get(event_type, f"({event_type})")
return _('PayPal reported an event: {}').format(text)
.. automethod:: pretix.base.logentrytypes.LogEntryType.display
If your new model object does not belong to an :class:`Event <pretix.base.models.Event>`, you need to inherit directly from ``LogEntryType`` instead
of ``EventLogEntryType``, providing your own implementation of ``get_object_link_info`` if object links should be
displayed.
.. autoclass:: pretix.base.logentrytypes.LogEntryType
:members: get_object_link_info
from pretix.base.signals import logentry_display
@receiver(signal=logentry_display)
def pretixcontrol_logentry_display(sender, logentry, **kwargs):
plains = {
'pretix.event.order.paid': _('The order has been marked as paid.'),
'pretix.event.order.refunded': _('The order has been refunded.'),
'pretix.event.order.canceled': _('The order has been canceled.'),
...
}
if logentry.action_type in plains:
return plains[logentry.action_type]
Sending notifications
---------------------

View File

@@ -15,7 +15,7 @@ includes serializers for serializing the following types:
* Built-in types: ``int``, ``float``, ``decimal.Decimal``, ``dict``, ``list``, ``bool``
* ``datetime.date``, ``datetime.datetime``, ``datetime.time``
* ``LazyI18nString``
* References to Django ``File`` objects that are already stored in a storage backend [#f1]_
* References to Django ``File`` objects that are already stored in a storage backend
* References to model instances
In code, we recommend to always use the ``.get()`` method on the settings object to access a value, but for
@@ -55,9 +55,6 @@ You can simply use it like this:
"preserve his reservation."),
)
.. _settings-defaults-in-plugins:
Defaults in plugins
-------------------
@@ -73,9 +70,3 @@ Make sure that you include this code in a module that is imported at app loading
.. _django-hierarkey: https://github.com/raphaelm/django-hierarkey
.. _documentation: https://django-hierarkey.readthedocs.io/en/latest/
.. rubric:: Footnotes
.. [#f1] If you store ``File`` instances in per-event settings, make sure to always register them with ``add_default``
as described above in :ref:`settings-defaults-in-plugins`. Otherwise, the file won't get copied properly if the
user copies the settings of an existing event to a new one.

View File

@@ -1,32 +0,0 @@
Time machine mode
=================
In test mode, pretix provides a "time machine" feature which allows event organizers
to test their shop as if it were a different date and time. To enable this feature, they can
click on the "time machine"-link in the test mode warning box on the event page.
Internally, this time machine mode is implemented by calling our custom :py:meth:`time_machine_now()`
function instead of :py:meth:`django.utils.timezone.now()` in all places where the fake time should be
taken into account. If you add code that uses the current date and time for checking whether some
product can be bought, you should use :py:meth:`time_machine_now`.
.. autofunction:: pretix.base.timemachine.time_machine_now
Background tasks
----------------
The time machine datetime is passed through the request flow via a thread-local variable (ContextVar).
Therefore, if you call a background task in the order process, where time_machine_now should be
respected, you need to pass it through manually as shown in the example below:
.. code-block:: python
@app.task()
def my_task(self, override_now_dt: datetime=None) -> None:
with time_machine_now_assigned(override_now_dt):
# ...do something that uses time_machine_now()
my_task.apply_async(kwargs={'override_now_dt': time_machine_now(default=None)})
.. autofunction:: pretix.base.timemachine.time_machine_now_assigned

View File

@@ -90,10 +90,6 @@ as its first argument and can be used like this::
<a href="{% eventurl request.event "presale:event.checkout" step="payment" %}">Pay</a>
<a href="{% abseventurl request.event "presale:event.checkout" step="payment" %}">Pay</a>
To generate absolute URLs on the main domain, you can use the ``absurl`` template tag::
{% load eventurl %}
<a href="{% absmainurl "control:event.settings" organizer=request.event.organizer.slug event=request.event.slug %}">Event settings</a>
Implementation details
----------------------

View File

@@ -136,7 +136,9 @@ It is a good idea to put this command into your git hook ``.git/hooks/pre-commit
for example, to check for any errors in any staged files when committing::
#!/bin/bash
cd $GIT_DIR/../src
export GIT_WORK_TREE=../
export GIT_DIR=../.git
source ../env/bin/activate # Adjust to however you activate your virtual environment
for file in $(git diff --cached --name-only | grep -E '\.py$' | grep -Ev "migrations|mt940\.py|pretix/settings\.py|make_testdata\.py|testutils/settings\.py|tests/settings\.py|pretix/base/models/__init__\.py|.*_pb2\.py")
do
@@ -209,15 +211,5 @@ with the documentation a lot, you might find it useful to use sphinx-autobuild::
Then, go to http://localhost:8081 for a version of the documentation that automatically re-builds
whenever you change a source file.
Working with frontend assets
----------------------------
To update the frontend styles of shops with a custom styling, run the following commands inside
your virtual environment.::
python -m pretix collectstatic --noinput
python -m pretix updateassets
.. _Django's documentation: https://docs.djangoproject.com/en/1.11/ref/django-admin/#runserver
.. _pretixdroid: https://github.com/pretix/pretixdroid

View File

@@ -31,7 +31,7 @@ pretix/
Additional code implementing our customized :ref:`URL handling <urlconf>`.
static/
Contains all static files (CSS/SASS, JavaScript, images) of pretix' core.
Contains all static files (CSS/SASS, JavaScript, images) of pretix' core
We use libsass as a preprocessor for CSS. Our own sass code is built in the same
step as Bootstrap and FontAwesome, so their mixins etc. are fully available.
@@ -41,6 +41,6 @@ pretix/
tests/
This is the root directory for all test codes. It includes subdirectories ``api``, ``base``,
``control``, ``presale``, ``helpers``, ``multidomain`` and ``plugins`` to mirror the structure
``control``, ``presale``, ``helpers`, ``multidomain`` and ``plugins`` to mirror the structure
of the pretix source code as well as ``testdummy``, which is a pretix plugin used during
testing.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

After

Width:  |  Height:  |  Size: 278 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

After

Width:  |  Height:  |  Size: 180 KiB

View File

@@ -42,25 +42,23 @@ endif
else
-down->[yes || force] "Is one or more block set on the ticket?"
--> if "" then
-right->[yes && !force] "Return error BLOCKED"
-right->[no && !force] "Return error BLOCKED"
else
-down->[no || force] "Is the order in status PENDING and not yet approved?"
-down->[yes || force] "If this is not an exit, is the valid_from/valid_until\nconstraint on the ticket fulfilled?"
--> if "" then
-right->[yes && !force] "Return error UNAPPROVED"
-right->[no && !force] "Return error INVALID_TIME"
else
-down->[no || force] "If this is not an exit, is the valid_from/valid_until\nconstraint on the ticket fulfilled?"
-down->[yes || force] "Is the product part of the check-in list?"
--> if "" then
-right->[no && !force] "Return error INVALID_TIME"
-right->[no && !force] "Return error PRODUCT"
else
-down->[yes || force] "Is the product part of the check-in list?"
-down->[yes || force] "Is the subevent part of the check-in list?"
--> if "" then
-right->[no && !force] "Return error PRODUCT"
-right->[no && !force] "Return error PRODUCT "
else
-down->[yes || force] "Is the subevent part of the check-in list?"
-down->[yes] "Is the order in status PAID?"
--> if "" then
-right->[no && !force] "Return error PRODUCT "
else
-down->[yes] "Is the order in status PAID?"
-right->[no && !force] "Is Order.require_approval set?"
--> if "" then
-->[no] "Is Order.valid_if_pending set?"
--> if "" then
@@ -79,8 +77,10 @@ else
endif
endif
else
-down->[yes || force] "Is this an entry or exit?\nIs the upload forced?"
-->[yes] "Return error UNPAID "
endif
else
-down->[yes || force] "Is this an entry or exit?\nIs the upload forced?"
endif
endif
endif

View File

@@ -32,7 +32,6 @@ transactions list of objects Transactions in
├ checksum string Checksum computed from payer, reference, amount and
date
├ payer string Payment source
├ external_id string Unique ID of the payment from an external source
├ reference string Payment reference
├ amount string Payment amount
├ iban string Payment IBAN
@@ -86,7 +85,6 @@ Endpoints
"date": "26.06.2017",
"payer": "John Doe",
"order": null,
"external_id": null,
"iban": "",
"bic": "",
"checksum": "5de03a601644dfa63420dacfd285565f8375a8f2",
@@ -141,7 +139,6 @@ Endpoints
"iban": "",
"bic": "",
"order": null,
"external_id": null,
"checksum": "5de03a601644dfa63420dacfd285565f8375a8f2",
"reference": "GUTSCHRIFT\r\nSAMPLECONF-NAB12 EREF: SAMPLECONF-NAB12\r\nIBAN: DE1234556…",
"state": "nomatch",

View File

@@ -34,19 +34,13 @@ internal_id string Can be used for
contact_name string Contact person (or ``null``)
contact_name_parts object of strings Decomposition of contact name (i.e. given name, family name)
contact_email string Contact person email address (or ``null``)
contact_cc_email string Copy email addresses, can be multiple separated by comma (or ``null``)
booth string Booth number (or ``null``). Maximum 100 characters.
locale string Locale for communication with the exhibitor.
locale string Locale for communication with the exhibitor (or ``null``).
access_code string Access code for the exhibitor to access their data or use the lead scanning app (read-only).
lead_scanning_access_code string Access code for the exhibitor to use the lead scanning app but not access data (read-only).
allow_lead_scanning boolean Enables lead scanning app
allow_lead_access boolean Enables access to data gathered by the lead scanning app
allow_voucher_access boolean Enables access to data gathered by exhibitor vouchers
lead_scanning_scope_by_device string Enables lead scanning to be handled as one lead per attendee
per scanning device, instead of only per exhibitor.
comment string Internal comment, not shown to exhibitor
exhibitor_tags list of strings Internal tags to categorize exhibitors, not shown to exhibitor.
The tags need to be created through the web interface currently.
===================================== ========================== =======================================================
You can also access the scanned leads through the API which contains the following public fields:
@@ -68,7 +62,6 @@ data list of objects Attendee data s
except in a few cases where it contains an additional list of objects
with ``value`` and ``label`` keys (e.g. splitting of names).
device_name string User-defined name for the device used for scanning (or ``null``).
device_uuid string UUID of device used for scanning (or ``null``).
===================================== ========================== =======================================================
Endpoints
@@ -112,17 +105,13 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",
"lead_scanning_access_code": "WVK2B8PZ",
"lead_scanning_scope_by_device": false,
"access_code": "VKHZ2FU8",
"allow_lead_scanning": true,
"allow_lead_access": true,
"allow_voucher_access": true,
"comment": "",
"exhibitor_tags": []
"comment": ""
}
]
}
@@ -167,17 +156,13 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",
"lead_scanning_access_code": "WVK2B8PZ",
"lead_scanning_scope_by_device": false,
"access_code": "VKHZ2FU8",
"allow_lead_scanning": true,
"allow_lead_access": true,
"allow_voucher_access": true,
"comment": "",
"exhibitor_tags": []
"comment": ""
}
:param organizer: The ``slug`` field of the organizer to fetch
@@ -245,8 +230,7 @@ Endpoints
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/vouchers/
Returns a list of all vouchers connected to an exhibitor. The response contains the same data as described in
:ref:`rest-vouchers` as well as for each voucher an additional field ``exhibitor_comment`` that is shown to the exhibitor. It can only
be modified using the ``attach`` API call below.
:ref:`rest-vouchers`.
**Example request**:
@@ -301,7 +285,7 @@ Endpoints
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/vouchers/attach/
Attaches an **existing** voucher to an exhibitor. You need to send either the ``id`` **or** the ``code`` field of
the voucher. You can call this method multiple times to update the optional ``exhibitor_comment`` field.
the voucher.
**Example request**:
@@ -312,8 +296,7 @@ Endpoints
Accept: application/json, text/javascript
{
"id": 15,
"exhibitor_comment": "Free ticket"
"id": 15
}
**Example request**:
@@ -325,8 +308,7 @@ Endpoints
Accept: application/json, text/javascript
{
"code": "43K6LKM37FBVR2YG",
"exhibitor_comment": "Free ticket"
"code": "43K6LKM37FBVR2YG"
}
**Example response**:
@@ -372,16 +354,13 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU8",
"allow_lead_scanning": true,
"allow_lead_access": true,
"allow_voucher_access": true,
"comment": "",
"exhibitor_tags": [
"Gold Sponsor"
]
"comment": ""
}
**Example response**:
@@ -405,19 +384,13 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",
"lead_scanning_access_code": "WVK2B8PZ",
"lead_scanning_scope_by_device": false,
"access_code": "VKHZ2FU8",
"allow_lead_scanning": true,
"allow_lead_access": true,
"allow_voucher_access": true,
"comment": "",
"exhibitor_tags": [
"Gold Sponsor"
]
"comment": ""
}
:param organizer: The ``slug`` field of the organizer to create new exhibitor for
@@ -438,7 +411,7 @@ Endpoints
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/exhibitors/1/ HTTP/1.1
PATCH /api/v1/organizers/bigevents/events/sampleconf/digitalcontents/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
@@ -469,19 +442,13 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",
"lead_scanning_access_code": "WVK2B8PZ",
"lead_scanning_scope_by_device": false,
"access_code": "VKHZ2FU8",
"allow_lead_scanning": true,
"allow_lead_access": true,
"allow_voucher_access": true,
"comment": "",
"exhibitor_tags": [
"Gold Sponsor"
]
"comment": ""
}
:param organizer: The ``slug`` field of the organizer to modify
@@ -492,36 +459,6 @@ Endpoints
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/exhibitor does not exist **or** you have no permission to change it.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/send_access_code/
Sends an email to the exhibitor with their access code.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/exhibitors/1/send_access_code/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param code: The ``id`` field of the exhibitor to send an email for
:statuscode 200: no error
:statuscode 400: The exhibitor does not have an email address associated
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 404: The requested exhibitor does not exist.
:statuscode 503: The email could not be sent.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/
@@ -592,7 +529,6 @@ name string Exhibitor name
booth string Booth number (or ``null``)
event object Object describing the event
├ name multi-lingual string Event name
├ end_date datetime End date of the event. After this time, the app could show a warning that the event is over.
├ imprint_url string URL to legal notice page. If not ``null``, a button in the app should link to this page.
├ privacy_url string URL to privacy notice page. If not ``null``, a button in the app should link to this page.
├ help_url string URL to help page. If not ``null``, a button in the app should link to this page.
@@ -628,7 +564,6 @@ scan_types list of objects Only used for a
"booth": "A2",
"event": {
"name": {"en": "Sample conference", "de": "Beispielkonferenz"},
"end_date": "2017-12-28T10:00:00+00:00",
"slug": "bigevents",
"imprint_url": null,
"privacy_url": null,
@@ -667,7 +602,6 @@ On the request, you should set the following properties:
* ``tags`` with the list of selected tags
* ``rating`` with the rating assigned by the exhibitor
* ``device_name`` with a user-specified name of the device used for scanning (max. 190 characters), or ``null``
* ``device_uuid`` with a auto-generated UUID of the device used for scanning, or ``null``
If you submit ``tags`` and ``rating`` to be ``null`` and ``notes`` to be ``""``, the server
responds with the previously saved information and will not delete that information. If you
@@ -702,8 +636,7 @@ The request for this looks like this:
"scan_type": "lead",
"tags": ["foo"],
"rating": 4,
"device_name": "DEV1",
"device_uuid": "d8c2ec53-d602-4a08-882d-db4cf54344a2"
"device_name": "DEV1"
}
**Example response:**
@@ -736,9 +669,7 @@ The request for this looks like this:
},
"rating": 4,
"tags": ["foo"],
"notes": "Great customer, wants our newsletter",
"device_name": "DEV1",
"device_uuid": "d8c2ec53-d602-4a08-882d-db4cf54344a2"
"notes": "Great customer, wants our newsletter"
}
:statuscode 200: No error, leads was not scanned for the first time
@@ -793,9 +724,7 @@ You can also fetch existing leads (if you are authorized to do so):
},
"rating": 4,
"tags": ["foo"],
"notes": "Great customer, wants our newsletter",
"device_name": "DEV1",
"device_uuid": "d8c2ec53-d602-4a08-882d-db4cf54344a2"
"notes": "Great customer, wants our newsletter"
}
]
}

View File

@@ -1,105 +0,0 @@
GetYourGuide
============
.. note::
The GetYourGuide integration is currently in Beta. Please contact support@pretix.eu to enable the integration
for your pretix.eu organizer account.
Introduction
------------
Using third party aggregators, such als GetYourGuide, event organizers can sell tickets to their events not only on
their own ticket-shop but also on the aggregator's portal. While this service is not for free, it allows event
organizers to reacher a larger audience that would otherwise not have found their way into the organizers webshop.
Using pretix' integration with GetYourGuide, event organizers can profit from an additional sales and revenue channel,
while keeping the effort for setting up and maintaining multiple ticket shops to a minimum.
Preparing your organizer account
--------------------------------
The first step in enabling the GetYourGuide integration, is to setup a corresponding Sales Channel, which will be used
to properly attribute the sales generated. This needs to be done only once per organizer account.
To do so, log into the pretix backend, select ``Organizers`` from the navigation and then the organizer in question.
Extending the ``Settings``-menu, find the ``Sales channels`` configuration and click the ``Add a new channel`` button.
On the following page, you will be able to select ``GetYourGuide`` as the sales channel type and give it a custom name.
Preparing your event
--------------------
In order to now sell your events on GetYourGuide, you will need to configure each event in question.
1. Enabling the plugin
Within your event, extend the ``Settings`` menu and navigate to ``Plugins``. Activate the plugin in the
``Integrations`` tab.
2. Sell the event on the sales channel
Pick the sales channel or channels, on which you would like to sell your event by navigating to the event's general
settings page using the ``Sell on all sales channels`` or ``Restrict to specific sales channels`` checkboxes.
3. Configure one or more products to be sold on GetYourGuide
Either create a new or edit an existing product, that you would like to sell on GetYourGuide. To do so, you will
need to have checked the ``Sell on all sales channels`` or appropriate ``Restrict to specific sales channels``
checkbox of the product within it's ``Availability`` tab.
In addition, you will also need to set the GetYourGuide equivalent ticket category in the product's accordingly
named settings tab. Within your event, there can be only one product per ticket category. Depending on your further
configuration, you must at least select one product to be in the ``Adult`` or ``Group`` category.
4. Configuring the GetYourGuide-plugin
Once you have configured one or more products to be eligible to be sold on GetYourGuide, you'll need to configure a
few basic settings within the event (``Settings`` --> ``GetYourGuide``). The most important settings can be found
the in the ``Configuration`` tab, such as the location of the event on sale.
Ticket Categories
-----------------
While pretix only uses the ticket category term loosely to group together multiple products for nicer display,
GetYourGuide is relying on the ticket categories to price the tickets.
First of all, you need to make the decision on how you are planning on selling your tickets on GetYourGuide - in most
cases, this will reflect your current sales strategy within your pretix shop.
- Individual tickets
Every single person attending will need to purchase their own ticket. A family of two adults and two
children will have to purchase and pay for a total of 4 tickets.
In this case, you will need to offer *at least* a ticket of the ``Adult`` type, but may offer any other ticket
category type (Child, Youth, Senior, ...) in addition. But you cannot offer a ``Group`` ticket.
- Group tickets
Two groups, consisting of 10 and 20 participants respectively, won't need to purchase a total of 30 tickets, but
rather two group tickets. It is up to you to configure the group size limits within the GetYourGuide-settings of your
product.
Choosing this option, you cannot offer any other ticket categories besides ``Group``.
Setting up event dates and quotas
---------------------------------
Of course, in addition to creating products, you will also need to add them to a quota for them to be available for
sale. The process for doing this is the very same as for any regular event or event series.
.. note::
When selling individual tickets through GetYourGuide, you will not be able to offer differing quantities for
individual ticket categories.
For this reason, we recommend to place all GetYourGuide-eligible products into the same quota. Should you however opt
to create multiple quotas which create an imbalance, pretix will report only the available number of tickets for the
lowest relevant quota.
Connecting your event to GetYourGuide
-------------------------------------
Once you have set up your event and products and performed all necessary configuration, you may want to use the
Analyzer-feature of our GetYourGuide-plugin (``Settings`` -> ``GetYourGuide`` -> tab ``Analyzer``).
The Analyzer should not display any blocking error messages and at least one event date that is ready for publishing on
the GetYourGuide platform.
At this point, you will need to setup your event (called ``product`` in the GetYourGuide universe) on their
`Supplier Portal`_ and connect it with your pretix shop. To do so, please follow the
`Connecting a new product to your Reservation System`_ on the GetYourGuide Supply Partner Help Center.
Select ``pretix.eu`` as your reservation system; the required ``product ID`` can be found in the ``Configuration`` tab
of the GetYourGuide plugin settings page.
From this point on, GetYourGuide will automatically import the availabilities and products and offer them for sale.
.. _Supplier Portal: https://suppliers.getyourguide.com/
.. _Connecting a new product to your Reservation System: https://supply.getyourguide.support/hc/en-us/articles/18008029689373-Connecting-a-new-product-to-your-Reservation-system

View File

@@ -24,5 +24,3 @@ If you want to **create** a plugin, please go to the
imported_secrets
webinar
presale-saml
kulturpass
getyourguide

View File

@@ -1,193 +0,0 @@
KulturPass
==========
.. note::
Since the KulturPass is specific to event organizers within Germany, the following page is also only provided in
German. Should you require assistance with the KulturPass and do not speak this language, please feel free reach
out to support@pretix.eu.
Einführung
----------
Der `KulturPass`_ ist ein Angebot der Bundesregierung für alle, die im laufenden Jahr ihren 18. Geburtstag feiern.
Sie erhalten ab ihrem 18. Geburtstag ein Budget von 200 Euro, das sie für Eintrittskarten, Bücher, CDs, Platten und
vieles andere einsetzen können. So wird Kultur vor Ort noch einfacher erlebbar. Gleichzeitig stärkt das die Nachfrage
bei den Anbietenden.
Da pretix ein Ticketing-System ist, stellen wir ausschließlich einen automatisierten Prozess für den Verkauf von
Eintrittskarten über den KulturPass-Marktplatz bereit.
Registrierung und Einrichtung
-----------------------------
Um als Unternehmen oder Kultureinrichtung Angebote auf dem KulturPass-Marktplatz anbieten zu können, ist zunächst eine
Registerung und die Einrichtung eines "Shops" sowie der dazugehörigen Angebote notwendig.
1. Registrierung
Registrieren Sie sich zunächst unter https://www.kulturpass.de/anbietende/layer als Anbieter. Im Zuge der
Registrierung beantworten Sie einige Fragen zu Ihrem Unternehmen/Ihrer Kultureinrichtung, hinterlegen Ihre
E-Mail-Adresse und beantworten Fragen zu Ihren Angebotsformen sowie Finanzierung Ihrer Einrichtung.
2. Anlegen eines KulturPass Shops
Nach Ihrer Registrierung müssen Sie der Weitergabe Ihrer Daten an die technische Platform hinter dem KulturPass,
Mirakl, zustimmen. Hier benennen Sie auch Ihren Shop.
3. Identifizierung mit ELSTER-Zertifikat
Als nächsten Schritt müssen Sie Ihr Unternehmen oder Ihre Einrichtung mit Hilfe eines sog. ELSTER-Zertifikates
identifizieren. Dieses Zertifikat nutzen Sie auch bereits jetzt schon, wenn Sie auf elektronischem Wege mit der
Finanzverwaltung kommunizieren.
4. Ersteinrichtung in pretix
Hinterlegen Sie nun die ID-Nummer Ihres KulturPass Marktplatz-Shops sowie einen API-Key in den
`Einstellungen Ihres Veranstalterkontos`_ (Veranstalter-Konto -> Einstellungen -> KulturPass). Diese Daten müssen
Sie nur einmalig für alle Ihre Veranstaltungen angeben.
Im `KulturPass-Backend`_ finden Sie die benötigten Informationen indem Sie auf das Benutzer-Symbol in der oberen,
rechten Ecke klicken, "Profil" und dann "API Schlüssel" auswählen bzw. indem Sie auf "Einstellungen" in der
Navigation links und dann "Shop" auswählen.
.. note::
Zu jedem Zeitpunkt kann nur ein Hintergrundsystem mit dem KulturPass-System verbunden sein. Werden
unterschiedliche Systeme oder gar mehrere pretix-Veranstalterkonten mit dem gleichen KulturPass-System verbunden,
können keine Bestellungen mehr verarbeitet werden und Angebote nicht automatisiert an den KulturPass-Marktplatz
übermittelt werden. Eingehende Bestellungen von Jugendlichen werden in diesem Fall automatisch abgelehnt, da diese
nicht eindeutig zugeordnet werden können. Ebenso überschreibt die Bereitstellung der Angebote eines Systems die
Angebote eines anderen Systems.
Wenn Sie mehrere Systeme haben, die den KulturPass-Marktplatz bedienen sollen, wenden Sie sich bitte an den
KulturPass-Support, um sich einen weiteren Shop einrichten zu lassen.
5. Aktivierung der KulturPass-Erweiterungen
Alle Veranstaltungen, die Sie über den KulturPass anbieten möchten, benötigen die `KulturPass-Erweiterung`_.
Aktivieren Sie diese bitte in jeder relevanten Veranstaltung über Einstellungen -> Erweiterungen -> Tab
"Integrationen" -> KulturPass.
6. Konfiguration der Artikel
Nachdem die KulturPass-Erweiterung aktiviert wurde, müssen Sie sich entscheiden, welche Produkte Sie über den
KulturPass-Marktplatz anbieten möchten. In der Bearbeitungs-Ansicht des jeweiligen Produktes finden Sie hierzu im
Tab "Zusätzliche Einstellungen" eine Checkbox "Das Produkt kann mit dem KulturPass erworben werden".
.. note::
Die Eigenschaft, dass ein Produkt durch den KulturPass-Marktplatz erworben werden kann, kann für beliebig viele
Produkte aktiviert werden. Auf Grund der Funktionsweise des KulturPasses sollten Sie jedoch gerade bei vielen
Artikeln mit unterschiedlich hohen Preisen darauf achten, dass die Preisspanne nicht zu hoch ausfällt.
Aktivieren Sie die Option für drei Produkte für 1, 10 und 100 Euro, so wird Ihr Angebot im KulturPass-Marktplatz
für 100 Euro gelistet werden. Dies bedeutet im Umkehrschluss auch, dass das KulturPass-Guthaben eines Jugendlichen
auch mindestens 100 Euro betragen muss, damit er Ihr Angebot in Anspruch nehmen kann - auch wenn die betroffene
Person lediglich das 1 Euro-Angebot wahrnehmen möchte. Erst mit dem 100 Euro KulturPass-Einlösecode wählt die
kaufende Person in Ihrem pretix-Shop aus, welches Produkt erworben werden soll. Ein Restguthaben wird nach dem Kauf
automatisch zurückerstattet und dem KulturPass-Konto wieder gutgeschrieben.
7. Konfiguration des Marktplatz-Eintrages
Je nach dem, ob es sich bei Ihrer Veranstaltung um eine Einzelveranstaltung oder eine Veranstaltungsreihe handelt,
müssen Sie die folgende Einstellung einmalig oder pro Veranstaltungstermin vornehmen.
Einzelveranstaltungen konfigurieren Sie über den Menüpunkt "KulturPass" in den Einstellungen Ihrer Veranstaltung;
Veranstaltungsreihen beim Anlegen oder Editieren eines jeden einzelnen Termins am Ende der Seite.
Um eine Veranstaltung oder einen Veranstaltungstermin im KulturPass-Marktplatz anzubieten, aktivieren Sie zunächst
die Option "Diese Veranstaltung via KulturPass anbieten". Geben Sie im folgenden die benötigten Informationen an.
Bitte beachten Sie, dass Sie bei den Angaben präzise Titel und Beschreibungen verwenden, da der KulturPass-
Marktplatz ausschließlich die Informationen aus diesem Bereich verwendet. Etwaige andere Informationen die Sie
bspw. in den "Text auf Startseite"-Felder eingeben haben, erreichen das KulturPass-System nicht.
.. note::
Gerade bei Veranstaltungsreihen nutzen viele pretix-Veranstalter gerne verkürzte Termin-Namen. Ein Schwimmbad würde
beispielsweise Ihre Veranstaltungsreihe "Freibad Musterstadt" und die einzelnen Termine nur "Schwimmen" nennen.
Während dies im pretix-Shop in einem gemeinsamen Kontext wunderbar funktioniert, würde eine Veranstaltung mit dem
Titel "Schwimmen" im KulturPass-Marktplatz Informationen vermissen lassen. Wählen Sie daher für das Eingabefeld
"Veranstaltungstitel" in der KulturPass-Konfiguration einen sprechenden Wert.
8. Übermittlung der Angebote
Sobald Sie Ihre ersten Veranstaltungen konfiguriert und live geschaltet haben, übermittelt pretix automatisch in
regelmäßigen Abständen alle von Ihnen angebotenen Veranstaltungen an das KulturPass System (Mirakl). Bitte beachten
Sie jedoch, dass der Import der Produkte und Angebote einige Zeit in Anspruch nehmen kann. Zum einen müssen
Angebote initial händisch von den Betreibern der KulturPass-Platform freigegeben werden, zum anderen muss auch eine
Synchronisation zwischen dem Hintergrundsystem und der KulturPass-App erfolgen. Auf die Dauer dieser Prozesse hat
pretix keinen Einfluss.
9. Freischalten des Marktplatz-Shops
Nachdem pretix erstmalig Angebote an das KulturPass-System übermittelt hat, müssen Sie Ihren Shop KulturPass-Shop
einmalig freischalten. Loggen Sie sich hierzu in das `KulturPass-Backend`_ ein.
Verwalten von KulturPass-Bestellungen
-------------------------------------
Durch die Nutzung der pretix-Integration mit dem KulturPass-System müssen Sie sich - bis auf die Kennzeichnung von
Produkten, die per KulturPass erworben werden dürfen, sowie die Bereitstellung von Veranstaltungs-Informationen für den
KulturPass-Marktplatz - um nichts kümmern: pretix übermittelt automatisch Ihre Veranstaltungen, wickelt die Einlösung
der Tickets ab und führt die Abrechnung mit dem Hintergrund-System durch.
Für Ihre Kunden verhält sich der KulturPass wie eine Zahlungsmethode im Bestellprozess und wird dort neben Ihren
anderen Zahlungsmethoden mit angeboten.
Die Gelder für mit dem KulturPass bezahlte Tickets erhalten Sie in Form einer Sammel-Überweisung von der Stiftung
Digitale Chancen auf das von Ihnen beim KulturPass Onboarding angegeben Bankkonto.
In Ihrem `KulturPass-Backend`_ können Sie über den Menüpunkt "Buchhaltung" Ihre bereits erfolgten und kommenden
Auszahlungen betrachten.
.. note::
Es ist von äußerster Wichtigkeit, dass Sie weder die eingehenden Bestellungen noch die Produkte und Angebote im
KulturPass-Backend händisch bearbeiten - auch wenn dies möglich wäre.
Bei händischen Änderungen riskieren Sie, dass die Datenbasis zwischen pretix und dem KulturPass-System divergiert
und es zu fehlerhaften Buchungen kommt. Wann immer möglich, sollten Sie Korrekturbuchungen und Änderungen
ausschließlich über pretix vornehmen.
Sollte eine händische Änderung/Korrektur notwendig werden, wenden Sie sich bitte an den pretix-Support, damit wir
die Auswirkungen evaluieren und vorab mit Ihnen besprechen können!
Erstattungen für Stornos und Absagen können Sie wie gehabt über das pretix-Backend vornehmen. Der jeweilige Betrag wird
dem KulturPass-Konto dann automatisch gutgeschrieben.
Da nach Ausgabe eines KulturPass Einlöse-Codes dieser vom Kunden jederzeit oder vom System bei
Nicht-(Komplett)Einlösung binnen 48 Stunden storniert werden kann, kann das im KulturPass-Backend angezeigte,
auszuzahlende Guthaben fluktuieren. Da in der Regel Auszahlungen frühestens 48 Stunden nach der Aufgabe einer
KulturPass-Bestellungen erfolgen, sollte Ihr Guthaben in der Regel nicht ins Negative gehen.
Ablauf für Kunden
-----------------
Ihre Kunden erhalten - nachdem sie sich ein eigenes Konto in der KulturPass-App angelegt und sich mit ihrem
elektronischen Personalausweis identifiziert haben - ein Guthaben von 200 Euro, welches für Leistungen aus dem
KulturPass-Marktplatz eingelöst werden kann.
Im Falle von Veranstaltungen, die per pretix verkauft werden, wählt der Kunde ein Angebot aus und erhält im folgenden
binnen kurzer Zeit (ca. 10-20 Minuten) einen Code und einen Link, um diesen einzulösen. Der Link bringt den Kunden direkt auf die Seite der
betreffenden pretix-Veranstaltung. Hier wird der Kunde darauf hingewiesen, für welche Produkte der Code genutzt werden
kann.
Im Bezahlschritt des Verkaufsprozesses wird dem Kunden vorgeschlagen, seinen KulturPass Einlösecode nun zu nutzen, um
die gewünschte Leistung zu erhalten.
Wurde ein Artikel gewählt, welcher günstiger als der Wert des Einlösecodes war, wird das Restguthaben automatisch auf
das KulturPass-Konto erstattet.
Wurden hingegen mehrere Artikel in den Warenkorb gelegt, so kann die Differenz mit einem anderen, regulären
Zahlungsmittel erfolgen.
Einlösecodes, die vom Kunden nicht binnen 48 Stunden eingelöst werden, werden automatisch storniert und dem
KulturPass-Konto wieder gutgeschrieben. Dieser Mechanismus greift auch, wenn eine Veranstaltung mittlerweile
ausverkauft ist und daher der Einlösecode nicht mehr Nutzbar ist.
Unterstützung
-------------
Weitergehende Informationen zum KulturPass finden Sie auch auf der `Webseite des KulturPasses`_, sowie im
`KulturPass Serviceportal`_.
.. _KulturPass: https://www.kulturpass.de/
.. _Einstellungen Ihres Veranstalterkontos: https://pretix.eu/control/organizer/-/settings/kulturpass
.. _KulturPass-Erweiterung: https://pretix.eu/control/event/-/-/settings/plugins#tab-0-2-open
.. _KulturPass-Backend: https://kulturpass-de.mirakl.net/
.. _Webseite des KulturPasses: https://www.kulturpass.de/
.. _KulturPass Serviceportal: https://service.kulturpass.de/help/

View File

@@ -158,7 +158,7 @@ expects and - more importantly - supports.
for a sample configuration in an academic context.
Note, that you can have multiple attributes with the same ``friendlyName``
but different ``name`` value. This is often used in systems, where the same
but different ``name``s. This is often used in systems, where the same
information (for example a persons name) is saved in different fields -
for example because one institution is returning SAML 1.0 and other
institutions are returning SAML 2.0 style attributes. Typically, this only

View File

@@ -29,8 +29,8 @@ item_assignments list of objects Products this l
===================================== ========================== =======================================================
Layout endpoints
----------------
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/ticketlayouts/
@@ -268,75 +268,5 @@ Layout endpoints
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.
Ticket rendering endpoint
-----------------------------
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/ticketpdfrenderer/render_batch/
With this API call, you can instruct the system to render a set of tickets into one combined PDF file. To specify
which tickets to render, you need to submit a list of "parts". For every part, the following fields are supported:
* ``orderposition`` (``integer``, required): The ID of the order position to render.
* ``override_channel`` (``string``, optional): The sales channel ID to be used for layout selection instead of the
original channel of the order.
* ``override_layout`` (``integer``, optional): The ticket layout ID to be used instead of the auto-selected one.
If your input parameters validate correctly, a ``202 Accepted`` status code is returned.
The body points you to the download URL of the result. Running a ``GET`` request on that result URL will
yield one of the following status codes:
* ``200 OK`` The export succeeded. The body will be your resulting file. Might be large!
* ``409 Conflict`` Your export is still running. The body will be JSON with the structure ``{"status": "running"}``. ``status`` can be ``waiting`` before the task is actually being processed. Please retry, but wait at least one second before you do.
* ``410 Gone`` Running the export has failed permanently. The body will be JSON with the structure ``{"status": "failed", "message": "Error message"}``
* ``404 Not Found`` The export does not exist / is expired.
.. warning:: This endpoint is considered **experimental**. It might change at any time without prior notice.
.. note:: To avoid performance issues, a maximum number of 1000 parts is currently allowed.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/ticketpdfrenderer/render_batch/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"parts": [
{
"orderposition": 55412
},
{
"orderposition": 55412,
"override_channel": "web"
},
{
"orderposition": 55412,
"override_layout": 56
}
]
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"download": "https://pretix.eu/api/v1/organizers/bigevents/events/sampleconf/ticketpdfrenderer/download/29891ede-196f-4942-9e26-d055a36e98b8/3f279f13-c198-4137-b49b-9b360ce9fcce/"
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:statuscode 202: no error
:statuscode 400: Invalid input options
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. _here: https://github.com/pretix/pretix/blob/master/src/pretix/static/schema/pdf-layout.schema.json

View File

@@ -1,4 +1,4 @@
sphinx==7.4.*
sphinx==7.0.*
jinja2==3.1.*
sphinx-rtd-theme
sphinxcontrib-httpdomain
@@ -6,4 +6,5 @@ sphinxcontrib-images
sphinxcontrib-jquery
sphinxcontrib-spelling==8.*
sphinxemoji
pygments-markdown-lexer
pyenchant==3.2.*

View File

@@ -1,5 +1,5 @@
-e ../
sphinx==7.4.*
sphinx==7.0.*
jinja2==3.1.*
sphinx-rtd-theme
sphinxcontrib-httpdomain
@@ -7,4 +7,5 @@ sphinxcontrib-images
sphinxcontrib-jquery
sphinxcontrib-spelling==8.*
sphinxemoji
pygments-markdown-lexer
pyenchant==3.2.*

View File

@@ -1,110 +0,0 @@
Android version support policy
==============================
Building software for Android always presents a struggle between keeping compatibility with older hardware to save cost
and utilizing feature of new Android versions to improve functionality, security and stability. To help you plan ahead,
we are publishing our intended schedule. This is to be understood as a minimum commitment, we will only drop support for
older versions if there is a technical reason to do so, not because the scheduled time has been reached.
.. warning:: This is a non-binding document. We will try our very best to not to deprecate support for Android versions
earlier than listed here, but for technical or economical reasons, it might become necessary to do so under
specific circumstances. Specifically, we might be forced to partially drop support for Android versions
earlier where we integrate third-party components into our software. Typical examples would be specific
payment terminal or printer types where we use a third-party component provided by the hardware vendor.
If we no longer support an Android version, it means that we will no longer publish new versions of the app supporting
that Android version. This means you are not getting new features or bug fixes, and at some point your app might stop
working with the pretix server.
pretixSCAN
----------
=========================== ==========================================================
Android Version Support schedule
=========================== ==========================================================
Android 14 Support planned until at least 12/2029.
Android 13 Support planned until at least 12/2028.
Android 12 Support planned until at least 12/2027.
Android 11 Support planned until at least 12/2026.
Android 10 Support planned until at least 12/2025.
Android 9 Support planned until at least 12/2025.
Android 8 Support planned until at least 12/2025.
Android 7 Support planned until at least 06/2025.
Android 6 Support planned until at least 06/2025.
Android 5 Support planned until at least 06/2025.
Android 4 Support dropped.
=========================== ==========================================================
pretixPOS
---------
=========================== ==========================================================
Android Version Support schedule
=========================== ==========================================================
Android 14 | Support planned until at least 12/2029.
| Limited support for Swissbit microSD TSE (only tested devices).
Android 13 | Support planned until at least 12/2028.
| Limited support for Swissbit microSD TSE (only tested devices).
Android 12 | Support planned until at least 12/2027.
| Limited support for Swissbit microSD TSE (only tested devices).
Android 11 | Support planned until at least 12/2026.
| No support for Swissbit microSD TSE.
Android 10 Support planned until at least 12/2025.
Android 9 Support planned until at least 12/2025.
Android 8 | Support planned until at least 12/2025.
| Support for Stripe Terminal on some devices to be dropped 05/2024.
Android 7 | Support planned until at least 12/2024.
| Support for Stripe Terminal to be dropped 05/2024.
| No support for Cryptovision TSE.
| No support for SumUp.
Android 6 | Support planned until at least 12/2024.
| No support for Cryptovision TSE.
| No support for Fiskal Cloud.
| No support for Stripe Terminal.
| No support for SumUp.
Android 5 | Support planned until at least 12/2024.
| No support for Cryptovision TSE.
| No support for Fiskal Cloud.
| No support for Stripe Terminal.
| No support for SumUp.
Android 4 Support dropped.
=========================== ==========================================================
pretixPRINT
-----------
=========================== ==========================================================
Android Version Support schedule
=========================== ==========================================================
Android 14 Support planned until at least 12/2029.
Android 13 Support planned until at least 12/2028.
Android 12 Support planned until at least 12/2027.
Android 11 Support planned until at least 12/2026.
Android 10 Support planned until at least 12/2025.
Android 9 Support planned until at least 12/2025.
Android 8 Support planned until at least 12/2025.
Android 7 Support planned until at least 06/2025.
Android 6 Support planned until at least 06/2025.
Android 5 | Support planned until at least 06/2025.
| No support for Evolis printers on some devices.
Android 4 Support dropped.
=========================== ==========================================================
pretixLEAD
----------
=========================== ==========================================================
Android Version Support schedule
=========================== ==========================================================
Android 14 Support planned until at least 12/2029.
Android 13 Support planned until at least 12/2028.
Android 12 Support planned until at least 12/2027.
Android 11 Support planned until at least 12/2026.
Android 10 Support planned until at least 12/2025.
Android 9 Support planned until at least 12/2025.
Android 8 Support planned until at least 12/2025.
Android 7 Support planned until at least 12/2024.
Android 6 Support planned until at least 12/2024.
Android 5 Support planned until at least 12/2024.
Android 4 Support dropped.
=========================== ==========================================================

View File

@@ -175,7 +175,7 @@ without any special behavior.
Connecting SSO providers (pretix as the SSO client)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To connect an external application as a SSO provider, go to "Customer accounts" → "SSO providers" → "Create a new SSO provider"
To connect an external application as a SSO client, go to "Customer accounts" → "SSO providers" → "Create a new SSO provider"
in your organizer account.
.. thumbnail:: ../../screens/organizer/customer_ssoprovider_add.png

View File

@@ -194,23 +194,17 @@ A complete record could look like this::
v=spf1 a mx include:_spf.pretix.eu ~all
Make sure to read up on the `SPF specification`_.
Make sure to read up on the `SPF specification`_. If you want to authenticate your emails with DKIM, set up a DNS TXT
record for the subdomain ``pretix._domainkey`` with the following contents::
If you want to authenticate your emails with `DKIM`_, set up a ``CNAME`` record for the subdomain ``pretix._domainkey``
pointing to ``dkim.pretix.eu``::
pretix._domainkey.mydomain.com. CNAME dkim.pretix.eu.
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXrDk6lwOWX00e2MbiiJac6huI+gnzLf9N4G1FnBv3PXq8fz3i2q1szH72OF5mAlKm3zXO4cl/uxx+lfidS1ERbX6Bn9BRstBTQUKWC4JFj8Yk9+fwT7LWehDURazLdTzfsIjJFudLLvxtOKSaOCtMhbPX05DIhziaqVCBqgz/NQIDAQAB
Then, please contact support@pretix.eu and we will enable DKIM for your domain on our mail servers.
For senders with larger volumes, Google Mail also requires you to have a `DMARC`_ policy (that may however be ``p=none``).
.. note:: Many SMTP servers impose rate limits on the sent emails, such as a maximum number of emails sent per hour.
These SMTP servers are often not suitable for use with pretix, in case you want to send an email to many
hundreds or thousands of ticket buyers. Depending on how the rate limit is implemented, emails might be lost
in this case, as pretix only retries email delivery for a certain time period.
.. _DKIM: https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail
.. _Sender Policy Framework: https://en.wikipedia.org/wiki/Sender_Policy_Framework
.. _SPF specification: http://www.open-spf.org/SPF_Record_Syntax
.. _DMARC: https://en.wikipedia.org/wiki/DMARC

View File

@@ -19,3 +19,4 @@ Then, head to the **Bundled products** tab of the "conference ticket" and add th
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:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -17,8 +17,8 @@ and then click "Generate widget code".
You will obtain two code snippets that look *roughly* like the following. The first should be embedded into the
``<head>`` part of your website, if possible. If this inconvenient, you can put it in the ``<body>`` part as well::
<link rel="stylesheet" type="text/css" href="https://pretix.eu/demo/democon/widget/v1.css" crossorigin>
<script type="text/javascript" src="https://pretix.eu/widget/v1.en.js" async crossorigin></script>
<link rel="stylesheet" type="text/css" href="https://pretix.eu/demo/democon/widget/v1.css">
<script type="text/javascript" src="https://pretix.eu/widget/v1.en.js" async></script>
The second snippet should be embedded at the position where the widget should show up::
@@ -96,18 +96,6 @@ attribute::
<pretix-widget event="https://pretix.eu/demo/democon/" disable-iframe></pretix-widget>
Always show events info
------------------------
If you want the widget to show the events info such as title, location and frontpage text, you can pass the optional
``display-event-info`` attribute with either a value of ``"false"``, ``"true"`` or ``"auto"`` the latter being the
default if the attribute is not present at all.
Note that any other value than ``"false"`` or ``"auto"`` means ``"true"``::
<pretix-widget event="https://pretix.eu/demo/democon/" display-event-info></pretix-widget>
Pre-selecting a voucher
-----------------------
@@ -136,24 +124,6 @@ If you want to disable voucher input in the widget, you can pass the ``disable-v
<pretix-widget event="https://pretix.eu/demo/democon/" disable-vouchers></pretix-widget>
Enabling the button-style single item select
--------------------------------------------
By default, the widget uses a checkbox to select items, that can only be bought in quantities of one. If you want to match
the button-style of that checkbox with the one in the pretix shop, you can use the ``single-item-select`` attribute::
<pretix-widget event="https://pretix.eu/demo/democon/" single-item-select="button"></pretix-widget>
.. image:: img/widget_checkbox_button.png
:align: center
:class: screenshot
.. note::
Due to compatibility with existing widget installations, the default value for ``single-item-select``
is ``checkbox``. This might change in the future, so make sure, to set the attribute to
``single-item-select="checkbox"`` if you need it.
Filtering products
------------------
@@ -208,10 +178,6 @@ settings. For example, if you set up a meta data property called "Promoted" that
<pretix-widget event="https://pretix.eu/demo/series/" list-type="list" filter="attr[Promoted]=Yes"></pretix-widget>
If you have enabled public filters in your meta data attribute configuration, a filter-form shows up. To disable, use::
<pretix-widget event="https://pretix.eu/demo/democon/" disable-filters></pretix-widget>
pretix Button
-------------
@@ -351,9 +317,9 @@ Currently, the following attributes are understood by pretix itself:
``data-attendee-name``, which will pre-fill the last part of the name, whatever that is.
* ``data-invoice-address-FIELD`` will pre-fill the corresponding field of the invoice address. Possible values for
``FIELD`` are ``company``, ``street``, ``zipcode``, ``city``, ``country``, ``internal-reference``, ``vat-id``, and
``custom-field``, as well as fields specified by the naming scheme such as ``name-title`` or ``name-given-name``
(see above). ``country`` expects a two-character country code.
``FIELD`` are ``company``, ``street``, ``zipcode``, ``city`` and ``country``, as well as fields specified by the
naming scheme such as ``name-title`` or ``name-given-name`` (see above). ``country`` expects a two-character
country code.
* If ``data-fix="true"`` is given, the user will not be able to change the other given values later. This currently
only works for the order email address as well as the invoice address. Attendee-level fields and questions can
@@ -441,57 +407,4 @@ Hosted or pretix Enterprise are active, you can pass the following fields:
});
</script>
Offering wallet payments (Apple Pay, Google Pay) within the widget
------------------------------------------------------------------
Some payment providers (such as Stripe) also offer Apple or Google Pay. But in order to use them, the domain of the
payment needs to be approved first. As of right now, pretix will take care of the domain verification process for you
automatically, when using Stripe. However, pretix can only validate the domain that is being used for your default,
"stand-alone" shop (such as https://pretix.eu/demo/democon/ ).
When embedding the widget on your website, the domain of the embedding page will also need to be validated in order to
be able to use it for wallet payments.
The details might vary from payment provider to payment provider, but generally speaking, it will either involve just
telling your payment provider the domain name and (for Apple Pay) placing an
``apple-developer-merchantid-domain-association``-file into the ``.well-known``-directory of your domain.
Further reading:
* `Stripe Payment Method Domain registration`_
Content Security Policy
-----------------------
When using a Content Security Policy (CSP) on your website, you may need to make some adjustments. If your pretix
shop is running under a custom domain, you need to add the following rules:
* ``script-src``: ``'unsafe-eval' https://pretix.eu`` (adjust to your domain for self-hosted pretix)
* ``style-src``: ``https://pretix.eu`` (adjust to your domain for self-hosted pretix **and** for custom domain on pretix Hosted)
* ``connect-src``: ``https://pretix.eu`` (adjust to your domain for self-hosted pretix **and** for custom domain on pretix Hosted)
* ``frame-src``: ``https://pretix.eu`` (adjust to your domain for self-hosted pretix **and** for custom domain on pretix Hosted)
* ``img-src``: ``https://pretix.eu`` (adjust to your domain for self-hosted pretix **and** for custom domain on pretix Hosted) and for pretix Hosted additionally add ``https://cdn.pretix.space``
External payment providers and Cross-Origin-Opener-Policy
---------------------------------------------------------
If you use a payment provider that opens a new window during checkout (such as PayPal), be aware that setting
``Cross-Origin-Opener-Policy: same-origin`` results in an empty popup-window being opened in the foreground. This is
due to JavaScript not having access to the opened window. To mitigate this, you either need to always open the widgets
checkout in a new tab (see :ref:`Always open a new tab`) or set ``Cross-Origin-Opener-Policy: same-origin-allow-popups``
Working with Cross-Origin-Embedder-Policy
-----------------------------------------
The pretix widget is unfortunately not compatible with ``Cross-Origin-Embedder-Policy: require-corp``. If you include
the ``crossorigin`` attributes on the ``<script>`` and ``<link>`` tag as shown above, the widget can show a calendar
or product list, but will not be able to open the checkout process in an iframe. If you also set
``Cross-Origin-Opener-Policy: same-origin``, the widget can auto-detect that it is running in an isolated enviroment
and will instead open the checkout process in a new tab.
.. _Let's Encrypt: https://letsencrypt.org/
.. _Stripe Payment Method Domain registration: https://stripe.com/docs/payments/payment-methods/pmd-registration

View File

@@ -16,5 +16,4 @@ wanting to use pretix to sell tickets.
events/giftcards
faq
markdown
android-version-support
glossary

View File

@@ -11,9 +11,6 @@ In many places of your shop, like frontpage texts, product descriptions and emai
since it is way easier to learn than languages like HTML but allows all basic formatting options required
for text in those places.
.. note:: Some fields that are used in one-line context only allow formatting that refers to individual words
(such as bold or italic font or a link) but do not allow block-level formatting like lists or headlines.
Formatting rules
----------------
@@ -148,7 +145,7 @@ to get a better plain text representation of your text. Note however, that for
security reasons you can only use the following HTML elements::
a, abbr, acronym, b, br, code, div, em, h1, h2,
h3, h4, h5, h6, hr, i, li, ol, p, pre, s, span, strong,
h3, h4, h5, h6, hr, i, li, ol, p, pre, span, strong,
table, tbody, td, thead, tr, ul
Additionally, only the following attributes are allowed on them::

View File

@@ -22,49 +22,50 @@ classifiers = [
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Framework :: Django :: 4.2",
"Framework :: Django :: 4.1",
]
dependencies = [
"arabic-reshaper==3.0.0", # Support for Arabic in reportlab
"babel",
"BeautifulSoup4==4.12.*",
"bleach==6.2.*",
"celery==5.4.*",
"chardet==5.2.*",
"cryptography>=44.0.0",
"css-inline==0.14.*",
"bleach==5.0.*",
"celery==5.3.*",
"chardet==5.1.*",
"cryptography>=3.4.2",
"css-inline==0.8.*",
"defusedcsv>=1.1.0",
"Django[argon2]==4.2.*,>=4.2.15",
"django-bootstrap3==24.3",
"django-compressor==4.5.1",
"django-countries==7.6.*",
"django-filter==24.3",
"dj-static",
"Django==4.2.*",
"django-bootstrap3==23.1.*",
"django-compressor==4.3.*",
"django-countries==7.5.*",
"django-filter==23.2",
"django-formset-js-improved==0.5.0.3",
"django-formtools==2.5.1",
"django-hierarkey==1.2.*",
"django-hijack==3.7.*",
"django-i18nfield==1.10.*",
"django-formtools==2.4.1",
"django-hierarkey==1.1.*",
"django-hijack==3.3.*",
"django-i18nfield==1.9.*,>=1.9.4",
"django-libsass==0.9",
"django-localflavor==4.0",
"django-markup",
"django-oauth-toolkit==2.3.*",
"django-otp==1.5.*",
"django-phonenumber-field==7.3.*",
"django-redis==5.4.*",
"django-oauth-toolkit==2.2.*",
"django-otp==1.2.*",
"django-phonenumber-field==7.1.*",
"django-redis==5.2.*",
"django-scopes==2.0.*",
"django-statici18n==2.6.*",
"djangorestframework==3.15.*",
"dnspython==2.7.*",
"django-statici18n==2.3.*",
"djangorestframework==3.14.*",
"dnspython==2.3.*",
"drf_ujson2==1.7.*",
"geoip2==4.*",
"importlib_metadata==8.*", # Polyfill, we can probably drop this once we require Python 3.10+
"importlib_metadata==6.*", # Polyfill, we can probably drop this once we require Python 3.10+
"isoweek",
"jsonschema",
"kombu==5.4.*",
"libsass==0.23.*",
"kombu==5.3.*",
"libsass==0.22.*",
"lxml",
"markdown==3.7", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
"markdown==3.4.3", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
# We can upgrade markdown again once django-bootstrap3 upgrades or once we drop Python 3.6 and 3.7
"mt-940==4.30.*",
"oauthlib==3.2.*",
@@ -72,58 +73,63 @@ dependencies = [
"packaging",
"paypalrestsdk==1.13.*",
"paypal-checkout-serversdk==1.0.*",
"PyJWT==2.10.*",
"PyJWT==2.7.*",
"phonenumberslite==8.13.*",
"Pillow==11.1.*",
"Pillow==9.5.*",
"pretix-plugin-build",
"protobuf==5.29.*",
"protobuf==4.23.*",
"psycopg2-binary",
"pycountry",
"pycparser==2.22",
"pycryptodome==3.21.*",
"pypdf==5.1.*",
"python-bidi==0.6.*", # Support for Arabic in reportlab
"python-dateutil==2.9.*",
"pycparser==2.21",
"pycryptodome==3.18.*",
"pypdf==3.9.*",
"python-bidi==0.4.*", # Support for Arabic in reportlab
"python-dateutil==2.8.*",
"python-u2flib-server==4.*",
"pytz",
"pytz-deprecation-shim==0.1.*",
"pyuca",
"qrcode==8.0",
"redis==5.2.*",
"reportlab==4.2.*",
"qrcode==7.4.*",
"redis==4.6.*",
"reportlab==4.0.*",
"requests==2.31.*",
"sentry-sdk==2.20.*",
"sentry-sdk==1.15.*",
"sepaxml==2.6.*",
"stripe==7.9.*",
"slimit",
"static3==0.7.*",
"stripe==5.4.*",
"text-unidecode==1.*",
"tlds>=2020041600",
"tqdm==4.*",
"ua-parser==1.0.*",
"vat_moss_forked==2020.3.20.0.11.0",
"vobject==0.9.*",
"webauthn==2.5.*",
"zeep==4.3.*"
"webauthn==0.4.*",
"zeep==4.2.*"
]
[project.optional-dependencies]
memcached = ["pylibmc"]
dev = [
"aiohttp==3.11.*",
"aiohttp==3.8.*",
"coverage",
"coveralls",
"fakeredis==2.26.*",
"flake8==7.1.*",
"fakeredis==2.18.*",
"flake8==6.0.*",
"freezegun",
"isort==5.13.*",
"pep8-naming==0.14.*",
"isort==5.12.*",
"pep8-naming==0.13.*",
"potypo",
"pytest-asyncio>=0.24",
"pycodestyle==2.10.*",
"pyflakes==3.0.*",
"pytest-asyncio",
"pytest-cache",
"pytest-cov",
"pytest-django==4.*",
"pytest-mock==3.14.*",
"pytest-mock==3.10.*",
"pytest-rerunfailures==11.*",
"pytest-sugar",
"pytest-xdist==3.6.*",
"pytest==8.3.*",
"pytest-xdist==3.3.*",
"pytest==7.3.*",
"responses",
]

View File

@@ -1,4 +0,0 @@
{
"ignore_dirs": ["node_modules", "data", "pretix/static", "pretix/locale", "pretix/static.dist"]
}

View File

@@ -6,7 +6,7 @@ localecompile:
./manage.py compilemessages
localegen:
./manage.py makemessages --keep-pot --ignore "pretix/static/npm_dir/*" $(LNGS)
./manage.py makemessages --keep-pot --ignore "pretix/helpers/*" --ignore "pretix/static/npm_dir/*" $(LNGS)
./manage.py makemessages --keep-pot -d djangojs --ignore "pretix/static/npm_dir/*" --ignore "pretix/helpers/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static.dist/*" --ignore "data/*" --ignore "pretix/static/rrule/*" --ignore "build/*" $(LNGS)
staticfiles: jsi18n

View File

@@ -19,4 +19,4 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
__version__ = "2024.12.0.dev0"
__version__ = "2023.8.0.dev0"

View File

@@ -62,7 +62,6 @@ INSTALLED_APPS = [
'pretix.plugins.badges',
'pretix.plugins.manualpayment',
'pretix.plugins.returnurl',
'pretix.plugins.autocheckin',
'pretix.plugins.webcheckin',
'django_countries',
'oauth2_provider',
@@ -75,21 +74,11 @@ FORMAT_MODULE_PATH = [
'pretix.helpers.formats',
]
CORE_MODULES = {
"pretix.base",
"pretix.presale",
"pretix.control",
"pretix.plugins.checkinlists",
"pretix.plugins.reports",
}
ALL_LANGUAGES = [
('en', _('English')),
('de', _('German')),
('de-informal', _('German (informal)')),
('ar', _('Arabic')),
('eu', _('Basque')),
('ca', _('Catalan')),
('zh-hans', _('Chinese (simplified)')),
('zh-hant', _('Chinese (traditional)')),
('cs', _('Czech')),
@@ -102,16 +91,12 @@ ALL_LANGUAGES = [
('el', _('Greek')),
('id', _('Indonesian')),
('it', _('Italian')),
('ja', _('Japanese')),
('lv', _('Latvian')),
('nb-no', _('Norwegian Bokmål')),
('pl', _('Polish')),
('pt-pt', _('Portuguese (Portugal)')),
('pt-br', _('Portuguese (Brazil)')),
('ro', _('Romanian')),
('ru', _('Russian')),
('sk', _('Slovak')),
('sv', _('Swedish')),
('es', _('Spanish')),
('tr', _('Turkish')),
('uk', _('Ukrainian')),
@@ -123,9 +108,8 @@ LANGUAGES_RTL = {
'ar', 'hw'
}
LANGUAGES_INCUBATING = {
'fi', 'pt-br', 'gl',
'pl', 'fi', 'pt-br', 'gl',
}
LANGUAGES = ALL_LANGUAGES
LOCALE_PATHS = [
os.path.join(os.path.dirname(__file__), 'locale'),
]
@@ -163,12 +147,6 @@ EXTRA_LANG_INFO = {
'name': 'Portuguese',
'name_local': 'Português',
},
'nb-no': {
'bidi': False,
'code': 'nb-no',
'name': 'Norwegian Bokmal',
'name_local': 'norsk (bokmål)',
},
}
django.conf.locale.LANG_INFO.update(EXTRA_LANG_INFO)
@@ -255,12 +233,7 @@ COMPRESS_FILTERS = {
)
}
CURRENCIES = [
c for c in currencies
if c.alpha_3 not in {
'XAG', 'XAU', 'XBA', 'XBB', 'XBC', 'XBD', 'XDR', 'XPD', 'XPT', 'XSU', 'XTS', 'XUA',
}
]
CURRENCIES = list(currencies)
CURRENCY_PLACES = {
# default is 2
'BIF': 0,
@@ -293,17 +266,14 @@ CACHE_LARGE_VALUES_ALIAS = 'default'
FILE_UPLOAD_EXTENSIONS_IMAGE = (".png", ".jpg", ".gif", ".jpeg")
PILLOW_FORMATS_IMAGE = ('PNG', 'GIF', 'JPEG')
FILE_UPLOAD_EXTENSIONS_FAVICON = (".ico", ".png", ".jpg", ".gif", ".jpeg")
PILLOW_FORMATS_QUESTIONS_FAVICON = ('PNG', 'GIF', 'JPEG', 'ICO')
FILE_UPLOAD_EXTENSIONS_FAVICON = (".ico", ".png", "jpg", ".gif", ".jpeg")
FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE = (".png", ".jpg", ".gif", ".jpeg", ".bmp", ".tif", ".tiff", ".jfif")
FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE = (".png", "jpg", ".gif", ".jpeg", ".bmp", ".tif", ".tiff", ".jfif")
PILLOW_FORMATS_QUESTIONS_IMAGE = ('PNG', 'GIF', 'JPEG', 'BMP', 'TIFF')
FILE_UPLOAD_EXTENSIONS_EMAIL_ATTACHMENT = (
".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt", ".docx", ".gif", ".svg",
".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages",
".bmp", ".tif", ".tiff", ".ics",
".bmp", ".tif", ".tiff"
)
FILE_UPLOAD_EXTENSIONS_OTHER = FILE_UPLOAD_EXTENSIONS_EMAIL_ATTACHMENT
PRETIX_MAX_ORDER_SIZE = 500

View File

@@ -38,7 +38,6 @@ MAIL_FROM_ORGANIZERS = 'invalid@invalid'
FILE_UPLOAD_MAX_SIZE_EMAIL_AUTO_ATTACHMENT = 10
FILE_UPLOAD_MAX_SIZE_EMAIL_ATTACHMENT = 10
FILE_UPLOAD_MAX_SIZE_IMAGE = 10
FILE_UPLOAD_MAX_SIZE_FAVICON = 10
DEFAULT_CURRENCY = 'EUR'
SECRET_KEY = "build-time-secret-key"
HAS_REDIS = False

View File

@@ -19,20 +19,16 @@
# 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.contrib.auth.models import AnonymousUser
from django_scopes import scopes_disabled
from rest_framework import exceptions
from rest_framework.authentication import TokenAuthentication
from pretix.api.auth.devicesecurity import (
FullAccessSecurityProfile, get_all_security_profiles,
DEVICE_SECURITY_PROFILES, FullAccessSecurityProfile,
)
from pretix.base.models import Device
logger = logging.getLogger(__name__)
class DeviceTokenAuthentication(TokenAuthentication):
model = Device
@@ -50,7 +46,6 @@ class DeviceTokenAuthentication(TokenAuthentication):
raise exceptions.AuthenticationFailed('Device has not been initialized.')
if device.revoked:
logging.warning(f'Connection attempt of revoked device {device.pk}.')
raise exceptions.AuthenticationFailed('Device access has been revoked.')
return AnonymousUser(), device
@@ -58,8 +53,7 @@ class DeviceTokenAuthentication(TokenAuthentication):
def authenticate(self, request):
r = super().authenticate(request)
if r and isinstance(r[1], Device):
profiles = get_all_security_profiles()
profile = profiles.get(r[1].security_profile, FullAccessSecurityProfile())
profile = DEVICE_SECURITY_PROFILES.get(r[1].security_profile, FullAccessSecurityProfile)
if not profile.is_allowed(request):
raise exceptions.PermissionDenied('Request denied by device security profile.')
return r

View File

@@ -20,40 +20,13 @@
# <https://www.gnu.org/licenses/>.
#
import logging
from collections import OrderedDict
from django.dispatch import receiver
from django.utils.translation import gettext_lazy as _
from pretix.api.signals import register_device_security_profile
logger = logging.getLogger(__name__)
_ALL_PROFILES = None
class BaseSecurityProfile:
@property
def identifier(self) -> str:
"""
Unique identifier for this profile.
"""
raise NotImplementedError()
@property
def verbose_name(self) -> str:
"""
Human-readable name (can be a ``gettext_lazy`` object).
"""
raise NotImplementedError()
def is_allowed(self, request) -> bool:
"""
Return whether a given request should be allowed.
"""
raise NotImplementedError()
class FullAccessSecurityProfile(BaseSecurityProfile):
class FullAccessSecurityProfile:
identifier = 'full'
verbose_name = _('Full device access (reading and changing orders and gift cards, reading of products and settings)')
@@ -61,7 +34,7 @@ class FullAccessSecurityProfile(BaseSecurityProfile):
return True
class AllowListSecurityProfile(BaseSecurityProfile):
class AllowListSecurityProfile:
allowlist = ()
def is_allowed(self, request):
@@ -104,7 +77,6 @@ class PretixScanSecurityProfile(AllowListSecurityProfile):
('GET', 'api-v1:blockedsecrets-list'),
('GET', 'api-v1:order-list'),
('GET', 'api-v1:orderposition-pdf_image'),
('POST', 'api-v1:orderposition-printlog'),
('GET', 'api-v1:event.settings'),
('POST', 'api-v1:upload'),
('POST', 'api-v1:checkinrpc.redeem'),
@@ -140,7 +112,6 @@ class PretixScanNoSyncNoSearchSecurityProfile(AllowListSecurityProfile):
('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:blockedsecrets-list'),
('GET', 'api-v1:orderposition-pdf_image'),
('POST', 'api-v1:orderposition-printlog'),
('GET', 'api-v1:event.settings'),
('POST', 'api-v1:upload'),
('POST', 'api-v1:checkinrpc.redeem'),
@@ -176,7 +147,6 @@ class PretixScanNoSyncSecurityProfile(AllowListSecurityProfile):
('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:blockedsecrets-list'),
('GET', 'api-v1:orderposition-pdf_image'),
('POST', 'api-v1:orderposition-printlog'),
('GET', 'api-v1:event.settings'),
('POST', 'api-v1:upload'),
('POST', 'api-v1:checkinrpc.redeem'),
@@ -184,28 +154,85 @@ class PretixScanNoSyncSecurityProfile(AllowListSecurityProfile):
)
def get_all_security_profiles():
global _ALL_PROFILES
if _ALL_PROFILES:
return _ALL_PROFILES
types = OrderedDict()
for recv, ret in register_device_security_profile.send(None):
if isinstance(ret, (list, tuple)):
for r in ret:
types[r.identifier] = r
else:
types[ret.identifier] = ret
_ALL_PROFILES = types
return types
@receiver(register_device_security_profile, dispatch_uid="base_register_default_security_profiles")
def register_default_webhook_events(sender, **kwargs):
return (
FullAccessSecurityProfile(),
PretixScanSecurityProfile(),
PretixScanNoSyncSecurityProfile(),
PretixScanNoSyncNoSearchSecurityProfile(),
class PretixPosSecurityProfile(AllowListSecurityProfile):
identifier = 'pretixpos'
verbose_name = _('pretixPOS')
allowlist = (
('GET', 'api-v1:version'),
('GET', 'api-v1:device.eventselection'),
('GET', 'api-v1:idempotency.query'),
('GET', 'api-v1:device.info'),
('POST', 'api-v1:device.update'),
('POST', 'api-v1:device.revoke'),
('POST', 'api-v1:device.roll'),
('GET', 'api-v1:event-list'),
('GET', 'api-v1:event-detail'),
('GET', 'api-v1:subevent-list'),
('GET', 'api-v1:subevent-detail'),
('GET', 'api-v1:itemcategory-list'),
('GET', 'api-v1:item-list'),
('GET', 'api-v1:question-list'),
('GET', 'api-v1:quota-list'),
('GET', 'api-v1:taxrule-list'),
('GET', 'api-v1:ticketlayout-list'),
('GET', 'api-v1:ticketlayoutitem-list'),
('GET', 'api-v1:badgelayout-list'),
('GET', 'api-v1:badgeitem-list'),
('GET', 'api-v1:voucher-list'),
('GET', 'api-v1:voucher-detail'),
('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:orderpayment-list'),
('POST', 'api-v1:orderrefund-list'),
('POST', 'api-v1:orderrefund-done'),
('POST', 'api-v1:cartposition-list'),
('POST', 'api-v1:cartposition-bulk-create'),
('GET', 'api-v1:checkinlist-list'),
('POST', 'api-v1:checkinlistpos-redeem'),
('POST', 'plugins:pretix_posbackend:order.posprintlog'),
('POST', 'plugins:pretix_posbackend:order.poslock'),
('DELETE', 'plugins:pretix_posbackend:order.poslock'),
('DELETE', 'api-v1:cartposition-detail'),
('GET', 'api-v1:giftcard-list'),
('POST', 'api-v1:giftcard-transact'),
('PATCH', 'api-v1:giftcard-detail'),
('GET', 'plugins:pretix_posbackend:posclosing-list'),
('POST', 'plugins:pretix_posbackend:posreceipt-list'),
('POST', 'plugins:pretix_posbackend:posclosing-list'),
('POST', 'plugins:pretix_posbackend:posdebugdump-list'),
('POST', 'plugins:pretix_posbackend:posdebuglogentry-list'),
('POST', 'plugins:pretix_posbackend:posdebuglogentry-bulk-create'),
('GET', 'plugins:pretix_posbackend:poscashier-list'),
('POST', 'plugins:pretix_posbackend:stripeterminal.token'),
('POST', 'plugins:pretix_posbackend:stripeterminal.paymentintent'),
('PUT', 'plugins:pretix_posbackend:file.upload'),
('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:blockedsecrets-list'),
('GET', 'api-v1:event.settings'),
('GET', 'plugins:pretix_seating:event.event'),
('GET', 'plugins:pretix_seating:event.event.subevent'),
('GET', 'plugins:pretix_seating:event.plan'),
('GET', 'plugins:pretix_seating:selection.simple'),
('POST', 'api-v1:upload'),
('POST', 'api-v1:checkinrpc.redeem'),
('GET', 'api-v1:checkinrpc.search'),
('POST', 'api-v1:reusablemedium-lookup'),
('POST', 'api-v1:reusablemedium-list'),
)
DEVICE_SECURITY_PROFILES = {
k.identifier: k() for k in (
FullAccessSecurityProfile,
PretixScanSecurityProfile,
PretixScanNoSyncSecurityProfile,
PretixScanNoSyncNoSearchSecurityProfile,
PretixPosSecurityProfile,
)
}

View File

@@ -39,8 +39,7 @@ from pretix.base.models import Device, Event, User
from pretix.base.models.auth import SuperuserPermissionSet
from pretix.base.models.organizer import TeamAPIToken
from pretix.helpers.security import (
Session2FASetupRequired, SessionInvalid, SessionPasswordChangeRequired,
SessionReauthRequired, assert_session_valid,
SessionInvalid, SessionReauthRequired, assert_session_valid,
)
@@ -67,10 +66,6 @@ class EventPermission(BasePermission):
return False
except SessionReauthRequired:
return False
except Session2FASetupRequired:
return False
except SessionPasswordChangeRequired:
return False
perm_holder = (request.auth if isinstance(request.auth, (Device, TeamAPIToken))
else request.user)
@@ -149,10 +144,6 @@ class ProfilePermission(BasePermission):
return False
except SessionReauthRequired:
return False
except Session2FASetupRequired:
return False
except SessionPasswordChangeRequired:
return False
if isinstance(request.auth, OAuthAccessToken):
if not (request.auth.allow_scopes(['read']) or request.auth.allow_scopes(['profile'])) and request.method in SAFE_METHODS:
@@ -175,9 +166,5 @@ class AnyAuthenticatedClientPermission(BasePermission):
return False
except SessionReauthRequired:
return False
except Session2FASetupRequired:
return False
except SessionPasswordChangeRequired:
return False
return True

View File

@@ -1,49 +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 rest_framework import exceptions
from rest_framework.authentication import (
SessionAuthentication as BaseSessionAuthentication,
)
from pretix.multidomain.middlewares import CsrfViewMiddleware
class CustomCSRFCheck(CsrfViewMiddleware):
def _reject(self, request, reason):
# Return the failure reason instead of an HttpResponse
return reason
class SessionAuthentication(BaseSessionAuthentication):
# Override from DRF to user our custom CSRF middleware
def enforce_csrf(self, request):
def dummy_get_response(request): # pragma: no cover
return None
check = CustomCSRFCheck(dummy_get_response)
# populates request.META['CSRF_COOKIE'], which is used in process_view()
check.process_request(request)
reason = check.process_view(request, None, (), {})
if reason:
# CSRF failed, bail with explicit error message
raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)

View File

@@ -1,82 +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 import forms
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.db.models.constants import LOOKUP_SEP
from django.forms import MultipleChoiceField
from django_filters import Filter
from django_filters.conf import settings
class MultipleCharField(forms.CharField):
widget = forms.MultipleHiddenInput
def to_python(self, value):
if not value:
return []
elif not isinstance(value, (list, tuple)):
raise ValidationError(
MultipleChoiceField.default_error_messages["invalid_list"], code="invalid_list"
)
return [str(val) for val in value]
class MultipleCharFilter(Filter):
"""
This filter performs OR(by default) or AND(using conjoined=True) query
on the selected inputs.
"""
field_class = MultipleCharField
def __init__(self, *args, **kwargs):
self.conjoined = kwargs.pop("conjoined", False)
super().__init__(*args, **kwargs)
def filter(self, qs, value):
if not value:
# Even though not a noop, no point filtering if empty.
return qs
if not self.conjoined:
q = Q()
for v in set(value):
predicate = self.get_filter_predicate(v)
if self.conjoined:
qs = self.get_method(qs)(**predicate)
else:
q |= Q(**predicate)
if not self.conjoined:
qs = self.get_method(qs)(q)
return qs.distinct() if self.distinct else qs
def get_filter_predicate(self, v):
name = self.field_name
if name and self.lookup_expr != settings.DEFAULT_LOOKUP_EXPR:
name = LOOKUP_SEP.join([name, self.lookup_expr])
try:
return {name: getattr(v, self.field.to_field_name)}
except (AttributeError, TypeError):
return {name: v}

View File

@@ -54,7 +54,7 @@ class IdempotencyMiddleware:
auth_hash_parts = '{}:{}'.format(
request.headers.get('Authorization', ''),
request.COOKIES.get('__Host-' + settings.SESSION_COOKIE_NAME, request.COOKIES.get(settings.SESSION_COOKIE_NAME, ''))
request.COOKIES.get(settings.SESSION_COOKIE_NAME, '')
)
auth_hash = sha1(auth_hash_parts.encode()).hexdigest()
idempotency_key = request.headers.get('X-Idempotency-Key', '')

View File

@@ -1,91 +0,0 @@
# Generated by Django 4.2.4 on 2023-09-26 12:01
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("pretixapi", "0010_webhook_comment"),
]
operations = [
migrations.AlterField(
model_name="apicall",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="oauthaccesstoken",
name="user",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="oauthapplication",
name="user",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="oauthgrant",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="oauthidtoken",
name="user",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="oauthrefreshtoken",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(app_label)s_%(class)s",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="webhook",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="webhookcall",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
migrations.AlterField(
model_name="webhookeventlistener",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 4.2.10 on 2024-02-12 11:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretixapi", "0011_bigint"),
]
operations = [
migrations.AddField(
model_name="oauthapplication",
name="post_logout_redirect_uris",
field=models.TextField(default=""),
),
]

View File

@@ -42,11 +42,6 @@ class OAuthApplication(AbstractApplication):
verbose_name=_("Redirection URIs"),
help_text=_("Allowed URIs list, space separated")
)
post_logout_redirect_uris = models.TextField(
blank=True, validators=[URIValidator],
help_text=_("Allowed Post Logout URIs list, space separated"),
default="",
)
client_id = models.CharField(
verbose_name=_("Client ID"),
max_length=100, unique=True, default=generate_client_id, db_index=True

View File

@@ -21,9 +21,7 @@
#
import json
from django.db.models import prefetch_related_objects
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class AsymmetricField(serializers.Field):
@@ -63,67 +61,3 @@ class CompatibleJSONField(serializers.JSONField):
if value:
return json.loads(value)
return value
class SalesChannelMigrationMixin:
"""
Translates between the old field "sales_channels" and the new field combo "all_sales_channels"/"limit_sales_channels".
"""
@property
def organizer(self):
if "organizer" in self.context:
return self.context["organizer"]
elif "event" in self.context:
return self.context["event"].organizer
else:
raise ValueError("organizer not in context")
def to_internal_value(self, data):
if "sales_channels" in data:
prefetch_related_objects([self.organizer], "sales_channels")
all_channels = {
s.identifier for s in
self.organizer.sales_channels.all()
}
if data.get("all_sales_channels") and set(data["sales_channels"]) != all_channels:
raise ValidationError({
"limit_sales_channels": [
"If 'all_sales_channels' is set, the legacy attribute 'sales_channels' must not be set or set to "
"the list of all sales channels."
]
})
if data.get("limit_sales_channels") and set(data["sales_channels"]) != set(data["limit_sales_channels"]):
raise ValidationError({
"limit_sales_channels": [
"If 'limit_sales_channels' is set, the legacy attribute 'sales_channels' must not be set or set to "
"the same list."
]
})
if set(data["sales_channels"]) == all_channels:
data["all_sales_channels"] = True
data["limit_sales_channels"] = []
else:
data["all_sales_channels"] = False
data["limit_sales_channels"] = data["sales_channels"]
del data["sales_channels"]
if data.get("all_sales_channels"):
data["limit_sales_channels"] = []
return super().to_internal_value(data)
def to_representation(self, value):
value = super().to_representation(value)
if value.get("all_sales_channels"):
prefetch_related_objects([self.organizer], "sales_channels")
value["sales_channels"] = sorted([
s.identifier for s in
self.organizer.sales_channels.all()
])
else:
value["sales_channels"] = value["limit_sales_channels"]
return value

View File

@@ -33,7 +33,7 @@ from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.api.serializers.order import (
AnswerCreateSerializer, AnswerSerializer, InlineSeatSerializer,
)
from pretix.base.models import SalesChannel, Seat, Voucher
from pretix.base.models import Seat, Voucher
from pretix.base.models.orders import CartPosition
@@ -212,11 +212,7 @@ class CartPositionCreateSerializer(BaseCartPositionCreateSerializer):
addons = BaseCartPositionCreateSerializer(many=True, required=False)
bundled = BaseCartPositionCreateSerializer(many=True, required=False)
seat = serializers.CharField(required=False, allow_null=True)
sales_channel = serializers.SlugRelatedField(
slug_field='identifier',
queryset=SalesChannel.objects.none(),
required=False,
)
sales_channel = serializers.CharField(required=False, default='sales_channel')
voucher = serializers.CharField(required=False, allow_null=True)
class Meta:
@@ -225,17 +221,13 @@ class CartPositionCreateSerializer(BaseCartPositionCreateSerializer):
'cart_id', 'expires', 'addons', 'bundled', 'seat', 'sales_channel', 'voucher'
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["sales_channel"].queryset = self.context["event"].organizer.sales_channels.all()
def validate_cart_id(self, cid):
if cid and not cid.endswith('@api'):
raise ValidationError('Cart ID should end in @api or be empty.')
return cid
def create(self, validated_data):
validated_data.pop('sales_channel', None)
validated_data.pop('sales_channel')
addons_data = validated_data.pop('addons', None)
bundled_data = validated_data.pop('bundled', None)

View File

@@ -25,6 +25,7 @@ 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.media import MEDIA_TYPES
from pretix.base.models import Checkin, CheckinList
@@ -36,8 +37,8 @@ class CheckinListSerializer(I18nAwareModelSerializer):
class Meta:
model = CheckinList
fields = ('id', 'name', 'all_products', 'limit_products', 'subevent', 'checkin_count', 'position_count',
'include_pending', 'allow_multiple_entries', 'allow_entry_after_exit',
'rules', 'exit_all_at', 'addon_match', 'ignore_in_statistics', 'consider_tickets_used')
'include_pending', 'auto_checkin_sales_channels', 'allow_multiple_entries', 'allow_entry_after_exit',
'rules', 'exit_all_at', 'addon_match')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -71,6 +72,10 @@ class CheckinListSerializer(I18nAwareModelSerializer):
if full_data.get('subevent'):
raise ValidationError(_('The subevent does not belong to this event.'))
for channel in full_data.get('auto_checkin_sales_channels') or []:
if channel not in get_all_sales_channels():
raise ValidationError(_('Unknown sales channel.'))
CheckinList.validate_rules(data.get('rules'))
return data

View File

@@ -19,27 +19,18 @@
# 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 pretix.api.serializers import SalesChannelMigrationMixin
from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.base.models import Discount, SalesChannel
from pretix.base.models import Discount
class DiscountSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
limit_sales_channels = serializers.SlugRelatedField(
slug_field="identifier",
queryset=SalesChannel.objects.none(),
required=False,
allow_empty=True,
many=True,
)
class DiscountSerializer(I18nAwareModelSerializer):
class Meta:
model = Discount
fields = ('id', 'active', 'internal_name', 'position', 'all_sales_channels', 'limit_sales_channels',
'available_from', 'available_until', 'subevent_mode', 'condition_all_products',
'condition_limit_products', 'condition_apply_to_addons', 'condition_min_count', 'condition_min_value',
fields = ('id', 'active', 'internal_name', 'position', 'sales_channels', 'available_from',
'available_until', 'subevent_mode', 'condition_all_products', 'condition_limit_products',
'condition_apply_to_addons', 'condition_min_count', 'condition_min_value',
'benefit_discount_matching_percent', 'benefit_only_apply_to_cheapest_n_matches',
'benefit_same_products', 'benefit_limit_products', 'benefit_apply_to_addons',
'benefit_ignore_voucher_discounted', 'condition_ignore_voucher_discounted')
@@ -48,7 +39,6 @@ class DiscountSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
super().__init__(*args, **kwargs)
self.fields['condition_limit_products'].queryset = self.context['event'].items.all()
self.fields['benefit_limit_products'].queryset = self.context['event'].items.all()
self.fields['limit_sales_channels'].child_relation.queryset = self.context['event'].organizer.sales_channels.all()
def validate(self, data):
data = super().validate(data)

View File

@@ -35,7 +35,7 @@
import logging
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.core.exceptions import ValidationError
from django.db import transaction
from django.utils.crypto import get_random_string
from django.utils.functional import cached_property
@@ -43,19 +43,13 @@ from django.utils.translation import gettext as _
from django_countries.serializers import CountryFieldMixin
from pytz import common_timezones
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.fields import ChoiceField, Field
from rest_framework.relations import SlugRelatedField
from pretix.api.serializers import (
CompatibleJSONField, SalesChannelMigrationMixin,
)
from pretix.api.serializers import CompatibleJSONField
from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.api.serializers.settings import SettingsSerializer
from pretix.base.models import (
CartPosition, Device, Event, OrderPosition, SalesChannel, Seat, TaxRule,
TeamAPIToken, Voucher,
)
from pretix.base.models import Device, Event, TaxRule, TeamAPIToken
from pretix.base.models.event import SubEvent
from pretix.base.models.items import (
ItemMetaProperty, SubEventItem, SubEventItemVariation,
@@ -167,7 +161,7 @@ class ValidKeysField(Field):
}
class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
class EventSerializer(I18nAwareModelSerializer):
meta_data = MetaDataField(required=False, source='*')
item_meta_properties = MetaPropertyField(required=False, source='*')
plugins = PluginsField(required=False, source='*')
@@ -176,13 +170,6 @@ class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
valid_keys = ValidKeysField(source='*', read_only=True)
best_availability_state = serializers.IntegerField(allow_null=True, read_only=True)
public_url = serializers.SerializerMethodField('get_event_url', read_only=True)
limit_sales_channels = serializers.SlugRelatedField(
slug_field="identifier",
queryset=SalesChannel.objects.none(),
required=False,
allow_empty=True,
many=True,
)
def get_event_url(self, event):
return build_absolute_uri(event, 'presale:event.index')
@@ -193,7 +180,7 @@ class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
'date_to', 'date_admission', 'is_public', 'presale_start',
'presale_end', 'location', 'geo_lat', 'geo_lon', 'has_subevents', 'meta_data', 'seating_plan',
'plugins', 'seat_category_mapping', 'timezone', 'item_meta_properties', 'valid_keys',
'all_sales_channels', 'limit_sales_channels', 'best_availability_state', 'public_url')
'sales_channels', 'best_availability_state', 'public_url')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -201,7 +188,6 @@ class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
self.fields.pop('valid_keys')
if not self.context.get('request') or 'with_availability_for' not in self.context['request'].GET:
self.fields.pop('best_availability_state')
self.fields['limit_sales_channels'].child_relation.queryset = self.context['organizer'].sales_channels.all()
def validate(self, data):
data = super().validate(data)
@@ -244,8 +230,8 @@ class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
for key, v in value['meta_data'].items():
if key not in self.meta_properties:
raise ValidationError(_('Meta data property \'{name}\' does not exist.').format(name=key))
if self.meta_properties[key].choices:
if v not in self.meta_properties[key].choice_keys:
if self.meta_properties[key].allowed_values:
if v not in [_v.strip() for _v in self.meta_properties[key].allowed_values.splitlines()]:
raise ValidationError(_('Meta data property \'{name}\' does not allow value \'{value}\'.').format(name=key, value=v))
return value
@@ -283,17 +269,13 @@ class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
from pretix.base.plugins import get_all_plugins
plugins_available = {
p.module: p for p in get_all_plugins(self.instance)
p.module for p in get_all_plugins(self.instance)
if not p.name.startswith('.') and getattr(p, 'visible', True)
}
settings_holder = self.instance if self.instance and self.instance.pk else self.context['organizer']
for plugin in value.get('plugins'):
if plugin not in plugins_available:
raise ValidationError(_('Unknown plugin: \'{name}\'.').format(name=plugin))
if getattr(plugins_available[plugin], 'restricted', False):
if plugin not in settings_holder.settings.allowed_restricted_plugins:
raise ValidationError(_('Restricted plugin: \'{name}\'.').format(name=plugin))
return value
@@ -437,13 +419,12 @@ class CloneEventSerializer(EventSerializer):
testmode = validated_data.pop('testmode', None)
has_subevents = validated_data.pop('has_subevents', None)
tz = validated_data.pop('timezone', None)
all_sales_channels = validated_data.pop('all_sales_channels', None)
limit_sales_channels = validated_data.pop('limit_sales_channels', None)
sales_channels = validated_data.pop('sales_channels', None)
date_admission = validated_data.pop('date_admission', None)
new_event = super().create({**validated_data, 'plugins': None})
event = Event.objects.filter(slug=self.context['event'], organizer=self.context['organizer'].pk).first()
new_event.copy_data_from(event, skip_meta_data='meta_data' in validated_data)
new_event.copy_data_from(event)
if plugins is not None:
new_event.set_active_plugins(plugins)
@@ -451,9 +432,8 @@ class CloneEventSerializer(EventSerializer):
new_event.is_public = is_public
if testmode is not None:
new_event.testmode = testmode
if all_sales_channels is not None or limit_sales_channels is not None:
new_event.all_sales_channels = all_sales_channels
new_event.limit_sales_channels.set(limit_sales_channels)
if sales_channels is not None:
new_event.sales_channels = sales_channels
if has_subevents is not None:
new_event.has_subevents = has_subevents
if has_subevents is not None:
@@ -492,8 +472,7 @@ class SubEventSerializer(I18nAwareModelSerializer):
fields = ('id', 'name', 'date_from', 'date_to', 'active', 'date_admission',
'presale_start', 'presale_end', 'location', 'geo_lat', 'geo_lon', 'event', 'is_public',
'frontpage_text', 'seating_plan', 'item_price_overrides', 'variation_price_overrides',
'meta_data', 'seat_category_mapping', 'last_modified', 'best_availability_state',
'comment')
'meta_data', 'seat_category_mapping', 'last_modified', 'best_availability_state')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -549,8 +528,8 @@ class SubEventSerializer(I18nAwareModelSerializer):
for key, v in value['meta_data'].items():
if key not in self.meta_properties:
raise ValidationError(_('Meta data property \'{name}\' does not exist.').format(name=key))
if self.meta_properties[key].choices:
if v not in self.meta_properties[key].choice_keys:
if self.meta_properties[key].allowed_values:
if v not in [_v.strip() for _v in self.meta_properties[key].allowed_values.splitlines()]:
raise ValidationError(_('Meta data property \'{name}\' does not allow value \'{value}\'.').format(name=key, value=v))
return value
@@ -681,8 +660,8 @@ class TaxRuleSerializer(CountryFieldMixin, I18nAwareModelSerializer):
class Meta:
model = TaxRule
fields = ('id', 'name', 'rate', 'code', 'price_includes_tax', 'eu_reverse_charge', 'home_country',
'internal_name', 'keep_gross_if_rate_changes', 'custom_rules')
fields = ('id', 'name', 'rate', 'price_includes_tax', 'eu_reverse_charge', 'home_country', 'internal_name',
'keep_gross_if_rate_changes', 'custom_rules')
class EventSettingsSerializer(SettingsSerializer):
@@ -704,12 +683,10 @@ class EventSettingsSerializer(SettingsSerializer):
'locales',
'locale',
'region',
'allow_modifications',
'allow_modifications_after_checkin',
'last_order_modification_date',
'allow_modifications_after_checkin',
'show_quota_left',
'waiting_list_enabled',
'waiting_list_auto_disable',
'waiting_list_hours',
'waiting_list_auto',
'waiting_list_names_asked',
@@ -728,7 +705,6 @@ class EventSettingsSerializer(SettingsSerializer):
'frontpage_subevent_ordering',
'event_list_type',
'event_list_available_only',
'event_list_filters',
'event_calendar_future_only',
'frontpage_text',
'event_info_text',
@@ -756,7 +732,6 @@ class EventSettingsSerializer(SettingsSerializer):
'payment_term_accept_late',
'payment_explanation',
'payment_pending_hidden',
'payment_giftcard__enabled',
'mail_days_order_expire_warning',
'ticket_download',
'ticket_download_date',
@@ -775,7 +750,6 @@ class EventSettingsSerializer(SettingsSerializer):
'invoice_address_company_required',
'invoice_address_beneficiary',
'invoice_address_custom_field',
'invoice_address_custom_field_helptext',
'invoice_name_required',
'invoice_address_not_asked_free',
'invoice_show_payments',
@@ -821,8 +795,6 @@ class EventSettingsSerializer(SettingsSerializer):
'cancel_allow_user_paid_refund_as_giftcard',
'cancel_allow_user_paid_require_approval',
'cancel_allow_user_paid_require_approval_fee_unknown',
'cancel_terms_paid',
'cancel_terms_unpaid',
'change_allow_user_variation',
'change_allow_user_addons',
'change_allow_user_until',
@@ -849,7 +821,6 @@ class EventSettingsSerializer(SettingsSerializer):
'reusable_media_type_nfc_mf0aes_autocreate_giftcard',
'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency',
'reusable_media_type_nfc_mf0aes_random_uid',
'seating_allow_blocked_seats_for_channel',
]
readonly_fields = [
# These are read-only since they are currently only settable on organizers, not events
@@ -900,7 +871,6 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
'locale',
'last_order_modification_date',
'show_quota_left',
'show_dates_on_frontpage',
'max_items_per_order',
'attendee_names_asked',
'attendee_names_required',
@@ -920,7 +890,6 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
'invoice_address_company_required',
'invoice_address_beneficiary',
'invoice_address_custom_field',
'invoice_address_custom_field_helptext',
'invoice_name_required',
'invoice_address_not_asked_free',
'invoice_address_from_name',
@@ -977,111 +946,3 @@ class ItemMetaPropertiesSerializer(I18nAwareModelSerializer):
class Meta:
model = ItemMetaProperty
fields = ('id', 'name', 'default', 'required', 'allowed_values')
def prefetch_by_id(items, qs, id_attr, target_attr):
"""
Prefetches a related object on each item in the given list of items by searching by id or another
unique field. The id value is read from the attribute on item specified in `id_attr`, searched on queryset `qs` by
the primary key, and the resulting prefetched model object is stored into `target_attr` on the item.
"""
ids = [getattr(item, id_attr) for item in items if getattr(item, id_attr)]
if ids:
result = qs.in_bulk(id_list=ids)
for item in items:
setattr(item, target_attr, result.get(getattr(item, id_attr)))
class SeatBulkBlockInputSerializer(serializers.Serializer):
ids = serializers.ListField(child=serializers.IntegerField(), required=False, allow_empty=True)
seat_guids = serializers.ListField(child=serializers.CharField(), required=False, allow_empty=True)
def to_internal_value(self, data):
data = super().to_internal_value(data)
if data.get("seat_guids") and data.get("ids"):
raise ValidationError("Please pass either seat_guids or ids.")
if data.get("seat_guids"):
seat_ids = data["seat_guids"]
if len(seat_ids) > 10000:
raise ValidationError({"seat_guids": ["Please do not pass over 10000 seats."]})
seats = {s.seat_guid: s for s in self.context["queryset"].filter(seat_guid__in=seat_ids)}
for s in seat_ids:
if s not in seats:
raise ValidationError({"seat_guids": [f"The seat '{s}' does not exist."]})
elif data.get("ids"):
seat_ids = data["ids"]
if len(seat_ids) > 10000:
raise ValidationError({"ids": ["Please do not pass over 10000 seats."]})
seats = self.context["queryset"].in_bulk(seat_ids)
for s in seat_ids:
if s not in seats:
raise ValidationError({"ids": [f"The seat '{s}' does not exist."]})
else:
raise ValidationError("Please pass either seat_guids or ids.")
return {"seats": seats.values()}
class SeatSerializer(I18nAwareModelSerializer):
orderposition = serializers.IntegerField(source='orderposition_id')
cartposition = serializers.IntegerField(source='cartposition_id')
voucher = serializers.IntegerField(source='voucher_id')
class Meta:
model = Seat
read_only_fields = (
'id', 'subevent', 'zone_name', 'row_name', 'row_label',
'seat_number', 'seat_label', 'seat_guid', 'product',
'orderposition', 'cartposition', 'voucher',
)
fields = (
'id', 'subevent', 'zone_name', 'row_name', 'row_label',
'seat_number', 'seat_label', 'seat_guid', 'product', 'blocked',
'orderposition', 'cartposition', 'voucher',
)
def prefetch_expanded_data(self, items, request, expand_fields):
if 'orderposition' in expand_fields:
if 'can_view_orders' not in request.eventpermset:
raise PermissionDenied('can_view_orders permission required for expand=orderposition')
prefetch_by_id(items, OrderPosition.objects.prefetch_related('order'), 'orderposition_id', 'orderposition')
if 'cartposition' in expand_fields:
if 'can_view_orders' not in request.eventpermset:
raise PermissionDenied('can_view_orders permission required for expand=cartposition')
prefetch_by_id(items, CartPosition.objects, 'cartposition_id', 'cartposition')
if 'voucher' in expand_fields:
if 'can_view_vouchers' not in request.eventpermset:
raise PermissionDenied('can_view_vouchers permission required for expand=voucher')
prefetch_by_id(items, Voucher.objects, 'voucher_id', 'voucher')
def __init__(self, instance, *args, **kwargs):
if not kwargs.get('data'):
self.prefetch_expanded_data(instance if hasattr(instance, '__iter__') else [instance],
kwargs['context']['request'],
kwargs['context']['expand_fields'])
super().__init__(instance, *args, **kwargs)
if 'orderposition' in self.context['expand_fields']:
from pretix.api.serializers.media import (
NestedOrderPositionSerializer,
)
self.fields['orderposition'] = NestedOrderPositionSerializer(read_only=True, context=self.context['order_context'])
try:
del self.fields['orderposition'].fields['seat']
except KeyError:
pass
if 'cartposition' in self.context['expand_fields']:
from pretix.api.serializers.cart import CartPositionSerializer
self.fields['cartposition'] = CartPositionSerializer(read_only=True)
del self.fields['cartposition'].fields['seat']
if 'voucher' in self.context['expand_fields']:
from pretix.api.serializers.voucher import VoucherSerializer
self.fields['voucher'] = VoucherSerializer(read_only=True)
del self.fields['voucher'].fields['seat']

View File

@@ -20,14 +20,11 @@
# <https://www.gnu.org/licenses/>.
#
from django import forms
from django.conf import settings
from django.http import QueryDict
from pytz import common_timezones
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from pretix.base.exporter import OrganizerLevelExportMixin
from pretix.base.models import ScheduledEventExport, ScheduledOrganizerExport
from pretix.base.timeframes import DateFrameField, SerializerDateFrameField
@@ -200,92 +197,3 @@ class JobRunSerializer(serializers.Serializer):
raise ValidationError(self.errors)
return not bool(self._errors)
class ScheduledExportSerializer(serializers.ModelSerializer):
schedule_next_run = serializers.DateTimeField(read_only=True)
export_identifier = serializers.ChoiceField(choices=[])
locale = serializers.ChoiceField(choices=settings.LANGUAGES, default='en')
owner = serializers.SlugRelatedField(slug_field='email', read_only=True)
error_counter = serializers.IntegerField(read_only=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['export_identifier'].choices = [(e, e) for e in self.context['exporters']]
def validate(self, attrs):
if attrs.get("export_form_data"):
identifier = attrs.get('export_identifier', self.instance.export_identifier if self.instance else None)
exporter = self.context['exporters'].get(identifier)
if exporter:
try:
JobRunSerializer(exporter=exporter).to_internal_value(attrs["export_form_data"])
except ValidationError as e:
raise ValidationError({"export_form_data": e.detail})
else:
raise ValidationError({"export_identifier": ["Unknown exporter."]})
return attrs
def validate_mail_additional_recipients(self, value):
d = value.replace(' ', '')
if len(d.split(',')) > 25:
raise ValidationError('Please enter less than 25 recipients.')
return d
def validate_mail_additional_recipients_cc(self, value):
d = value.replace(' ', '')
if len(d.split(',')) > 25:
raise ValidationError('Please enter less than 25 recipients.')
return d
def validate_mail_additional_recipients_bcc(self, value):
d = value.replace(' ', '')
if len(d.split(',')) > 25:
raise ValidationError('Please enter less than 25 recipients.')
return d
class ScheduledEventExportSerializer(ScheduledExportSerializer):
class Meta:
model = ScheduledEventExport
fields = [
'id',
'owner',
'export_identifier',
'export_form_data',
'locale',
'mail_additional_recipients',
'mail_additional_recipients_cc',
'mail_additional_recipients_bcc',
'mail_subject',
'mail_template',
'schedule_rrule',
'schedule_rrule_time',
'schedule_next_run',
'error_counter',
]
class ScheduledOrganizerExportSerializer(ScheduledExportSerializer):
timezone = serializers.ChoiceField(default=settings.TIME_ZONE, choices=[(a, a) for a in common_timezones])
class Meta:
model = ScheduledOrganizerExport
fields = [
'id',
'owner',
'export_identifier',
'export_form_data',
'locale',
'mail_additional_recipients',
'mail_additional_recipients_cc',
'mail_additional_recipients_bcc',
'mail_subject',
'mail_template',
'schedule_rrule',
'schedule_rrule_time',
'schedule_next_run',
'timezone',
'error_counter',
]

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