Compare commits

..

3 Commits

Author SHA1 Message Date
Raphael Michel
581f3c3d58 Release 1.8.1 2017-11-25 19:07:42 +01:00
Raphael Michel
6600c430ab [SECURITY] Respect session timeout in API 2017-11-25 19:06:46 +01:00
Raphael Michel
807eb2ea7f [SECURITY] Fix handling of session timeouts 2017-11-25 19:06:03 +01:00
363 changed files with 6618 additions and 30800 deletions

1
.gitattributes vendored
View File

@@ -5,7 +5,6 @@ src/static/moment/* linguist-vendored
src/static/datetimepicker/* linguist-vendored
src/static/colorpicker/* linguist-vendored
src/static/fileupload/* linguist-vendored
src/static/vuejs/* linguist-vendored
src/static/charts/* linguist-vendored
src/pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/fabric.* linguist-vendored
src/pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/pdf.* linguist-vendored

View File

@@ -8,8 +8,6 @@ tests:
- XDG_CACHE_HOME=/cache bash .travis.sh tests
tags:
- python3
except:
- pypi
pypi:
stage: release
script:
@@ -24,7 +22,7 @@ pypi:
tags:
- python3
only:
- pypi
- release
artifacts:
paths:
- src/dist/

View File

@@ -25,27 +25,19 @@ if [ "$1" == "doctests" ]; then
cd doc
make doctest
fi
if [ "$1" == "spelling" ]; then
XDG_CACHE_HOME=/cache pip3 install -Ur doc/requirements.txt
cd doc
make spelling
if [ -s _build/spelling/output.txt ]; then
exit 1
fi
fi
if [ "$1" == "tests" ]; then
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt -r src/requirements/py34.txt
cd src
python manage.py check
make all compress
py.test --reruns 5 tests
py.test --rerun 5 tests
fi
if [ "$1" == "tests-cov" ]; then
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt -r src/requirements/py34.txt
cd src
python manage.py check
make all compress
coverage run -m py.test --reruns 5 tests && codecov
coverage run -m py.test --rerun 5 tests && codecov
fi
if [ "$1" == "plugins" ]; then
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt -r src/requirements/py34.txt
@@ -58,7 +50,7 @@ if [ "$1" == "plugins" ]; then
cd pretix-cartshare
python setup.py develop
make
py.test --reruns 5 tests
py.test --rerun 5 tests
popd
fi

View File

@@ -36,10 +36,5 @@ matrix:
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_postgres.cfg
- python: 3.6
env: JOB=plugins
- python: 3.6
env: JOB=spelling
addons:
postgresql: "9.4"
apt:
packages:
- enchant

View File

@@ -1,5 +0,0 @@
Code of Conduct
===============
We have a [Code of Conduct](https://docs.pretix.eu/en/latest/development/contribution/codeofconduct.html)
in place that applies to all project contributions, including issues, pull requests, etc.

View File

@@ -40,11 +40,6 @@ Contributing
If you want to contribute to pretix, please read the `developer documentation`_
in our documentation. If you have any further questions, please do not hesitate to ask!
Code of Conduct
---------------
We have a `Code of Conduct`_ in place that applies to all project contributions,
including issues, pull requests, etc.
License
-------
The code in this repository is published under the terms of the Apache License.
@@ -55,6 +50,5 @@ AUTHORS file for a list of all the awesome folks who contributed to this project
.. _installation guide: https://docs.pretix.eu/en/latest/admin/installation/index.html
.. _developer documentation: https://docs.pretix.eu/en/latest/development/index.html
.. _Code of Conduct: https://docs.pretix.eu/en/latest/development/contribution/codeofconduct.html
.. _pretix.eu: https://pretix.eu
.. _blog: https://pretix.eu/about/en/blog/

View File

@@ -2,7 +2,6 @@
cd /pretix/src
export DJANGO_SETTINGS_MODULE=production_settings
export DATA_DIR=/data/
export HOME=/pretix
NUM_WORKERS=10
if [ ! -d /data/logs ]; then

View File

@@ -23,7 +23,6 @@ autostart=true
autorestart=true
priority=5
user=pretixuser
environment=HOME=/pretix
[program:pretixtask]
command=/usr/local/bin/pretix taskworker

View File

@@ -175,9 +175,3 @@ pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
spelling:
$(SPHINXBUILD) -b spelling $(ALLSPHINXOPTS) $(BUILDDIR)/spelling
@echo
@echo "Spelling check finished, look at the results in " \
"$(BUILDDIR)/spelling/output.txt."

View File

@@ -2,8 +2,6 @@
.. _`config`:
.. spelling:: Galera
Configuration file
==================
@@ -47,7 +45,7 @@ Example::
``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``.
data. Defaults to thea value of the environment variable ``DATA_DIR`` or ``data``.
``plugins_default``
A comma-separated list of plugins that are enabled by default for all new events.

View File

@@ -162,7 +162,7 @@ named ``/etc/systemd/system/pretix.service`` with the following content::
-v /etc/pretix:/etc/pretix \
-v /var/run/redis:/var/run/redis \
-v /var/run/mysqld:/var/run/mysqld \
pretix/standalone:stable all
pretix/standalone all
ExecStop=/usr/bin/docker stop %n
[Install]
@@ -239,8 +239,6 @@ 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.
.. _`docker_plugininstall`:
Install a plugin
----------------

View File

@@ -1,7 +1,5 @@
.. highlight:: ini
.. spelling:: SQL
General remarks
===============

View File

@@ -177,7 +177,7 @@ For background tasks we need a second service ``/etc/systemd/system/pretix-worke
[Install]
WantedBy=multi-user.target
You can now run the following commands to enable and start the services::
You can now run the following comamnds to enable and start the services::
# systemctl daemon-reload
# systemctl enable pretix-web pretix-worker
@@ -213,7 +213,7 @@ The following snippet is an example on how to configure a nginx proxy for pretix
ssl_certificate /path/to/cert.chain.pem;
ssl_certificate_key /path/to/key.pem;
add_header Referrer-Policy same-origin;
add_header Referrer-Options same-origin;
add_header X-Content-Type-Options nosniff;
location / {
@@ -276,8 +276,6 @@ To upgrade to a new pretix release, pull the latest code changes and run the fol
# systemctl restart pretix-web pretix-worker
.. _`manual_plugininstall`:
Install a plugin
----------------

View File

@@ -44,7 +44,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -90,7 +90,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"id": 1,

View File

@@ -1,402 +0,0 @@
Check-in lists
==============
Resource description
--------------------
You can create check-in lists that you can use e.g. at the entrance of your event to track who is coming and if they
actually bought a ticket.
You can create multiple check-in lists to separate multiple parts of your event, for example if you have separate
entries for multiple ticket types. Different check-in lists are completely independent: If a ticket shows up on two
lists, it is valid once on every list. This might be useful if you run a festival with festival passes that allow
access to every or multiple performances as well as tickets only valid for single performances.
The check-in list resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the check-in list
name string The internal name of the check-in list
all_products boolean If ``True``, the check-in lists contains tickets of all products in this event. The ``limit_products`` field is ignored in this case.
limit_products list of integers List of item IDs to include in this list.
subevent integer ID of the date inside an event series this list belongs to (or ``null``).
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).
===================================== ========================== =======================================================
.. versionchanged:: 1.10
This resource has been added.
.. versionchanged:: 1.11
The ``positions`` endpoints have been added.
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/
Returns a list of all check-in lists within a given event.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/checkinlists/ 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,
"name": "Default list",
"checkin_count": 123,
"position_count": 456,
"all_products": true,
"limit_products": [],
"subevent": null
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query integer subevent: Only return check-in lists of the sub-event with the given ID
: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)/checkinlists/(id)/
Returns information on one check-in list, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"name": "Default list",
"checkin_count": 123,
"position_count": 456,
"all_products": true,
"limit_products": [],
"subevent": null
}
: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 check-in list 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)/checkinlists/
Creates a new check-in list.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/checkinlists/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"name": "VIP entry",
"all_products": false,
"limit_products": [1, 2],
"subevent": null
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 2,
"name": "VIP entry",
"checkin_count": 0,
"position_count": 0,
"all_products": false,
"limit_products": [1, 2],
"subevent": null
}
:param organizer: The ``slug`` field of the organizer of the event/item to create a list for
:param event: The ``slug`` field of the event to create a list for
:statuscode 201: no error
:statuscode 400: The list 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)/checkinlists/(id)/
Update a check-in list. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``id`` field and the ``checkin_count`` and ``position_count``
fields.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"name": "Backstage",
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 2,
"name": "Backstage",
"checkin_count": 23,
"position_count": 42,
"all_products": false,
"limit_products": [1, 2],
"subevent": null
}
: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 list to modify
:statuscode 200: no error
:statuscode 400: The list 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)/checkinlist/(id)/
Delete a check-in list. Note that this also deletes the information on all check-ins performed via this list.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/checkinlist/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 check-in list 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.
Order position endpoints
------------------------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/
Returns a list of all order positions within a given event. The result is the same as
the :ref:`order-position-resource`, with one important difference: the ``checkins`` value will only include
check-ins for the selected list.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/positions/ 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": 23442,
"order": "ABC12",
"positionid": 1,
"item": 1345,
"variation": null,
"price": "23.00",
"attendee_name": "Peter",
"attendee_email": null,
"voucher": null,
"tax_rate": "0.00",
"tax_rule": null,
"tax_value": "0.00",
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null,
"subevent": null,
"checkins": [
{
"list": 1,
"datetime": "2017-12-25T12:45:23Z"
}
],
"answers": [
{
"question": 12,
"answer": "Foo",
"options": []
}
],
"downloads": [
{
"output": "pdf",
"url": "https://pretix.eu/api/v1/organizers/bigevents/events/sampleconf/orderpositions/23442/download/pdf/"
}
]
}
]
}
: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 ``order__code``,
``order__datetime``, ``positionid``, ``attendee_name``, ``last_checked_in`` and ``order__email``. Default:
``attendee_name,positionid``
:query string order: Only return positions of the order with the given order code
:query integer item: Only return positions with the purchased item matching the given ID.
:query integer variation: Only return positions with the purchased item variation matching the given ID.
:query string attendee_name: Only return positions with the given value in the attendee_name field. Also, add-on
products positions are shown if they refer to an attendee with the given name.
:query string secret: Only return positions with the given ticket secret.
:query bollean has_checkin: If set to ``true`` or ``false``, only return positions that have or have not been
checked in already on this list.
:query integer subevent: Only return positions of the sub-event with the given ID
:query integer addon_to: Only return positions that are add-ons to the position with the given ID.
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param list: The ID of the check-in list to look for
: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.
:statuscode 404: The requested check-in list does not exist.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/(id)
Returns information on one order position, identified by its internal ID.
The result format is the same as the :ref:`order-position-resource`, with one important difference: the
``checkins`` value will only include check-ins for the selected list.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/positions/ 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": 23442,
"order": "ABC12",
"positionid": 1,
"item": 1345,
"variation": null,
"price": "23.00",
"attendee_name": "Peter",
"attendee_email": null,
"voucher": null,
"tax_rate": "0.00",
"tax_rule": null,
"tax_value": "0.00",
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null,
"subevent": null,
"checkins": [
{
"list": 1,
"datetime": "2017-12-25T12:45:23Z"
}
],
"answers": [
{
"question": 12,
"answer": "Foo",
"options": []
}
],
"downloads": [
{
"output": "pdf",
"url": "https://pretix.eu/api/v1/organizers/bigevents/events/sampleconf/orderpositions/23442/download/pdf/"
}
]
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param list: The ID of the check-in list to look for
:param id: The ``id`` field of the order position 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.
:statuscode 404: The requested order position or check-in list does not exist.

View File

@@ -54,7 +54,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -103,7 +103,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"name": {"en": "Sample Conference"},

View File

@@ -15,5 +15,4 @@ Resources and endpoints
orders
invoices
vouchers
checkinlists
waitinglist

View File

@@ -41,7 +41,6 @@ foreign_currency_rate decimal (string) If ``foreign_cu
invoicing time, it is stored here.
foreign_currency_rate_date date If ``foreign_currency_rate`` is set, this signifies the
date at which the currency rate was obtained.
internal_reference string Customer's reference to be printed on the invoice.
===================================== ========================== =======================================================
@@ -58,11 +57,6 @@ internal_reference string Customer's refe
``foreign_currency_rate_date`` have been added.
.. versionchanged:: 1.9
The attribute ``internal_reference`` has been added.
Endpoints
---------
@@ -84,7 +78,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -101,7 +95,6 @@ Endpoints
"refers": null,
"locale": "en",
"introductory_text": "thank you for your purchase of the following items:",
"internal_reference": "",
"additional_text": "We are looking forward to see you on our conference!",
"payment_provider_text": "Please transfer the money to our account ABC…",
"footer_text": "Big Events LLC - Registration No. 123456 - VAT ID: EU0987654321",
@@ -125,7 +118,7 @@ Endpoints
: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.
:query string refers: If set, only invoices referring to the given invoice will be returned.
:query string refers: If set, only invoices refering 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
``nr`` (equals to ``number``). Default: ``nr``
@@ -153,7 +146,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"number": "SAMPLECONF-00001",
@@ -165,7 +158,6 @@ Endpoints
"refers": null,
"locale": "en",
"introductory_text": "thank you for your purchase of the following items:",
"internal_reference": "",
"additional_text": "We are looking forward to see you on our conference!",
"payment_provider_text": "Please transfer the money to our account ABC…",
"footer_text": "Big Events LLC - Registration No. 123456 - VAT ID: EU0987654321",
@@ -221,5 +213,5 @@ Endpoints
: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.
:statuscode 409: The file is not yet ready and will now be prepared. Retry the request after waiting for a few
:statuscode 409: The file is not yet ready and will now be prepared. Retry the request after waiting vor a few
seconds.

View File

@@ -70,7 +70,7 @@ addons list of objects Definition of a
├ addon_category integer Internal ID of the item category the add-on can be
chosen from.
├ min_count integer The minimal number of add-ons that need to be chosen.
├ max_count integer The maximal number of add-ons that can be chosen.
├ max_count integer The maxima number of add-ons that can be chosen.
└ position integer An integer, used for sorting
===================================== ========================== =======================================================
@@ -101,7 +101,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -188,7 +188,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"id": 1,

View File

@@ -1,5 +1,3 @@
.. spelling:: checkins
Orders
======
@@ -26,7 +24,7 @@ email string The customer em
locale string The locale used for communication with this customer
datetime datetime Time of order creation
expires datetime The order will expire, if it is still pending by this time
payment_date date Date of payment receipt
payment_date date Date of payment receival
payment_provider string Payment provider used for this order
payment_fee money (string) Payment fee included in this order's total
payment_fee_tax_rate decimal (string) Tax rate applied to the payment fee
@@ -45,7 +43,6 @@ invoice_address object Invoice address
├ zipcode string Customer ZIP code
├ city string Customer city
├ country string Customer country
├ internal_reference string Customer's internal reference to be printed on the invoice
├ vat_id string Customer VAT ID
└ vat_id_validated string ``True``, if the VAT ID has been validated against the
EU VAT service and validation was successful. This only
@@ -81,14 +78,8 @@ downloads list of objects List of ticket
The attributes ``invoice_address.vat_id_validated`` and ``invoice_address.is_business`` have been added.
The attributes ``order.payment_fee``, ``order.payment_fee_tax_rate`` and ``order.payment_fee_tax_value`` have been
deprecated in favor of the new ``fees`` attribute but will still be served and removed in 1.9.
deprecated in favour of the new ``fees`` attribute but will still be served and removed in 1.9.
.. versionchanged:: 1.9
First write operations (``…/mark_paid/``, ``…/mark_pending/``, ``…/mark_canceled/``, ``…/mark_expired/``) have been added.
The attribute ``invoice_address.internal_reference`` has been added.
.. _order-position-resource:
Order position resource
-----------------------
@@ -98,7 +89,7 @@ Order position resource
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the order position
id integer Internal ID of the order positon
code string Order code of the order the position belongs to
positionid integer Number of the position within the order
item integer ID of the purchased item
@@ -114,7 +105,6 @@ secret string Secret code pri
addon_to integer Internal ID of the position this position is an add-on for (or ``null``)
subevent integer ID of the date inside an event series this position belongs to (or ``null``).
checkins list of objects List of check-ins with this ticket
├ list integer Internal ID of the check-in list
└ datetime datetime Time of check-in
downloads list of objects List of ticket download options
├ output string Ticket output provider (e.g. ``pdf``, ``passbook``)
@@ -129,10 +119,6 @@ answers list of objects Answers to user
The attribute ``tax_rule`` has been added.
.. versionchanged:: 1.11
The attribute ``checkins.list`` has been added.
Order endpoints
---------------
@@ -155,7 +141,7 @@ Order endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -184,7 +170,6 @@ Order endpoints
"zipcode": "12345",
"city": "Testington",
"country": "Testikistan",
"internal_reference": "",
"vat_id": "EU123456789",
"vat_id_validated": False
},
@@ -207,7 +192,6 @@ Order endpoints
"subevent": null,
"checkins": [
{
"list": 44,
"datetime": "2017-12-25T12:45:23Z"
}
],
@@ -267,7 +251,7 @@ Order endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"code": "ABC12",
@@ -291,7 +275,6 @@ Order endpoints
"zipcode": "12345",
"city": "Testington",
"country": "Testikistan",
"internal_reference": "",
"vat_id": "EU123456789",
"vat_id_validated": False
},
@@ -314,7 +297,6 @@ Order endpoints
"subevent": null,
"checkins": [
{
"list": 44,
"datetime": "2017-12-25T12:45:23Z"
}
],
@@ -347,13 +329,12 @@ Order endpoints
: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.
:statuscode 404: The requested order does not exist.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/download/(output)/
Download tickets for an order, identified by its order code. Depending on the chosen output, the response might
be a ZIP file, PDF file or something else. The order details response contains a list of output options for this
particular order.
partictular order.
Tickets can be only downloaded if the order is paid and if ticket downloads are active. Note that in some cases the
ticket file might not yet have been created. In that case, you will receive a status code :http:statuscode:`409` and
@@ -384,208 +365,11 @@ Order endpoints
: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
**or** downloads are not available for this order at this time. The response content will
**or** downlodas are not available for this order at this time. The response content will
contain more details.
:statuscode 404: The requested order or output 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
:statuscode 409: The file is not yet ready and will now be prepared. Retry the request after waiting vor a few
seconds.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/mark_paid/
Marks a pending or expired order as successfully paid.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/orders/ABC12/mark_paid/ 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
{
"code": "ABC12",
"status": "p",
...
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param code: The ``code`` field of the order to modify
:statuscode 200: no error
:statuscode 400: The order cannot be marked as paid, either because the current order status does not allow it or because no quota is left to perform the operation.
: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 order does not exist.
:statuscode 409: The server was unable to acquire a lock and could not process your request. You can try again after a short waiting period.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/mark_canceled/
Marks a pending order as canceled.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/orders/ABC12/mark_canceled/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: text/json
{
"send_email": true
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"code": "ABC12",
"status": "c",
...
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param code: The ``code`` field of the order to modify
:statuscode 200: no error
:statuscode 400: The order cannot be marked as canceled since the current order status does not allow it.
: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 order does not exist.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/mark_pending/
Marks a paid order as unpaid.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/orders/ABC12/mark_pending/ 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
{
"code": "ABC12",
"status": "n",
...
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param code: The ``code`` field of the order to modify
:statuscode 200: no error
:statuscode 400: The order cannot be marked as unpaid since the current order status does not allow it.
: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 order does not exist.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/mark_expired/
Marks a unpaid order as expired.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/orders/ABC12/mark_expired/ 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
{
"code": "ABC12",
"status": "e",
...
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param code: The ``code`` field of the order to modify
:statuscode 200: no error
:statuscode 400: The order cannot be marked as expired since the current order status does not allow it.
: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 order does not exist.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/extend/
Extends the payment deadline of a pending order. If the order is already expired and quota is still
available, its state will be changed to pending.
The only required parameter of this operation is ``expires``, which should contain a date in the future.
Note that only a date is expected, not a datetime, since pretix will always set the deadline to the end of the
day in the event's timezone.
You can pass the optional parameter ``force``. If it is set to ``true``, the operation will be performed even if
it leads to an overbooked quota because the order was expired and the tickets have been sold again.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/orders/ABC12/extend/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: text/json
{
"expires": "2017-10-28",
"force": false
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"code": "ABC12",
"status": "n",
"expires": "2017-10-28T23:59:59Z",
...
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param code: The ``code`` field of the order to modify
:statuscode 200: no error
:statuscode 400: The order cannot be extended since the current order status does not allow it or no quota is available or the submitted date is invalid.
: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 order does not exist.
Order position endpoints
------------------------
@@ -608,7 +392,7 @@ Order position endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -633,7 +417,6 @@ Order position endpoints
"subevent": null,
"checkins": [
{
"list": 44,
"datetime": "2017-12-25T12:45:23Z"
}
],
@@ -693,7 +476,7 @@ Order position endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"id": 23442,
@@ -713,7 +496,6 @@ Order position endpoints
"subevent": null,
"checkins": [
{
"list": 44,
"datetime": "2017-12-25T12:45:23Z"
}
],
@@ -738,13 +520,12 @@ Order position endpoints
: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.
:statuscode 404: The requested order position does not exist.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/orderpositions/(id)/download/(output)/
Download tickets for one order position, identified by its internal ID.
Depending on the chosen output, the response might be a ZIP file, PDF file or something else. The order details
response contains a list of output options for this particular order position.
response contains a list of output options for this partictular order position.
Tickets can be only downloaded if the order is paid and if ticket downloads are active. Also, depending on event
configuration downloads might be only unavailable for add-on products or non-admission products.
@@ -776,8 +557,7 @@ Order position endpoints
: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
**or** downloads are not available for this order position at this time. The response content will
**or** downlodas 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
:statuscode 409: The file is not yet ready and will now be prepared. Retry the request after waiting vor a few
seconds.

View File

@@ -41,7 +41,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -77,7 +77,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"name": "Big Events LLC",

View File

@@ -54,7 +54,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -113,7 +113,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"id": 1,

View File

@@ -4,7 +4,7 @@ Quotas
Resource description
--------------------
Quotas define how many times an item can be sold.
Questions define how many times an item can be sold.
The quota resource contains the following public fields:
.. rst-class:: rest-resource-table
@@ -20,10 +20,6 @@ variations list of integers List of item va
subevent integer ID of the date inside an event series this quota belongs to (or ``null``).
===================================== ========================== =======================================================
.. versionchanged:: 1.10
The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added.
Endpoints
---------
@@ -46,7 +42,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -92,7 +88,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"id": 1,
@@ -110,131 +106,6 @@ Endpoints
: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)/quotas/
Creates a new quota
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/quotas/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content: application/json
{
"name": "Ticket Quota",
"size": 200,
"items": [1, 2],
"variations": [1, 4, 5, 7],
"subevent": null
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"name": "Ticket Quota",
"size": 200,
"items": [1, 2],
"variations": [1, 4, 5, 7],
"subevent": null
}
:param organizer: The ``slug`` field of the organizer of the event/item to create a quota for
:param event: The ``slug`` field of the event to create a quota for
:statuscode 201: no error
:statuscode 400: The quota 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)/quotas/(id)/
Update a quota. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``id`` field.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/quotas/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"name": "New Ticket Quota",
"size": 100,
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 2,
"name": "New Ticket Quota",
"size": 100,
"items": [
1,
2
],
"variations": [
1,
2
],
"subevent": null
}
: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 quota rule to modify
:statuscode 200: no error
:statuscode 400: The quota 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)/quota/(id)/
Delete a quota. Note that if you delete a quota the items the quota acts on might no longer be available for sale.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/quotas/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 quotas 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.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/quotas/(id)/availability/
Returns availability information on one quota, identified by its ID.
@@ -253,7 +124,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"available": true,

View File

@@ -60,7 +60,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -114,7 +114,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"id": 1,

View File

@@ -25,10 +25,6 @@ home_country string Merchant countr
This resource has been added.
.. versionchanged:: 1.9
The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added.
Endpoints
---------
@@ -51,7 +47,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -94,7 +90,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"id": 1,
@@ -107,128 +103,7 @@ Endpoints
: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 tax rule to fetch
:param id: The ``slug`` field of the sub-event 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)/taxrules/
Create a new tax rule.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/taxrules/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 166
{
"name": {"en": "VAT"},
"rate": "19.00",
"price_includes_tax": true,
"eu_reverse_charge": false,
"home_country": "DE"
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"name": {"en": "VAT"},
"rate": "19.00",
"price_includes_tax": true,
"eu_reverse_charge": false,
"home_country": "DE"
}
:param organizer: The ``slug`` field of the organizer to create a tax rule for
:param event: The ``slug`` field of the event to create a tax rule for
:statuscode 201: no error
:statuscode 400: The tax 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 tax rules.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/taxrules/(id)/
Update a tax 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/taxrules/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 34
{
"rate": "20.00",
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"id": 1,
"name": {"en": "VAT"},
"rate": "20.00",
"price_includes_tax": true,
"eu_reverse_charge": false,
"home_country": "DE"
}
: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 tax rule to modify
:statuscode 200: no error
:statuscode 400: The tax 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)/taxrules/(id)/
Delete a tax rule. Note that tax rules can only be deleted if they are not in use for any products, settings
or orders. If you cannot delete a tax rule, this method will return a ``403`` status code and you can only
discontinue using it everywhere else.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/taxrules/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 tax 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 tax rule cannot be deleted since it is currently in use.
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view it.

View File

@@ -44,10 +44,6 @@ subevent integer ID of the date
===================================== ========================== =======================================================
.. versionchanged:: 1.9
The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added.
Endpoints
---------
@@ -69,7 +65,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -140,7 +136,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"id": 1,
@@ -166,151 +162,3 @@ Endpoints
: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)/vouchers/
Create a new voucher.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/vouchers/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 408
{
"code": "43K6LKM37FBVR2YG",
"max_usages": 1,
"valid_until": null,
"block_quota": false,
"allow_ignore_quota": false,
"price_mode": "set",
"value": "12.00",
"item": 1,
"variation": null,
"quota": null,
"tag": "testvoucher",
"comment": "",
"subevent": null
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"code": "43K6LKM37FBVR2YG",
"max_usages": 1,
"redeemed": 0,
"valid_until": null,
"block_quota": false,
"allow_ignore_quota": false,
"price_mode": "set",
"value": "12.00",
"item": 1,
"variation": null,
"quota": null,
"tag": "testvoucher",
"comment": "",
"subevent": null
}
:param organizer: The ``slug`` field of the organizer to create a voucher for
:param event: The ``slug`` field of the event to create a voucher for
:statuscode 201: no error
:statuscode 400: The voucher 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.
:statuscode 409: The server was unable to acquire a lock and could not process your request. You can try again after a short waiting period.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/vouchers/(id)/
Update a voucher. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``id`` and ``redeemed`` fields.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/vouchers/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 408
{
"price_mode": "set",
"value": "24.00",
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"code": "43K6LKM37FBVR2YG",
"max_usages": 1,
"redeemed": 0,
"valid_until": null,
"block_quota": false,
"allow_ignore_quota": false,
"price_mode": "set",
"value": "24.00",
"item": 1,
"variation": null,
"quota": null,
"tag": "testvoucher",
"comment": "",
"subevent": null
}
: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 voucher to modify
:statuscode 200: no error
:statuscode 400: The voucher 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.
:statuscode 409: The server was unable to acquire a lock and could not process your request. You can try again after a short waiting period.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/vouchers/(id)/
Delete a voucher. Note that you cannot delete a voucher if it already has been redeemed.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/vouchers/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 voucher 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

@@ -48,7 +48,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"count": 1,
@@ -102,7 +102,7 @@ Endpoints
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
Content-Type: text/javascript
{
"id": 1,

View File

@@ -1,11 +0,0 @@
from enchant.tokenize import get_tokenizer, Filter, unit_tokenize
class CheckinFilter(Filter):
""" If a word looks like checkin_count, it refers to a so-called variable in
the code, and is treated as being spelled right."""
def _split(self, word):
if word[:8] == "checkin_":
return unit_tokenize(word[8:])
return unit_tokenize(word)

View File

@@ -45,7 +45,6 @@ extensions = [
'sphinx.ext.coverage',
'sphinxcontrib.httpdomain',
'sphinxcontrib.images',
'sphinxcontrib.spelling',
]
# Add any paths that contain templates here, relative to this directory.
@@ -291,22 +290,3 @@ texinfo_documents = [
images_config = {
'default_image_width': '250px'
}
# -- Options for Spelling output ------------------------------------------
# String specifying the language, as understood by PyEnchant and enchant.
# Defaults to en_US for US English.
spelling_lang = 'en_US'
# String specifying a file containing a list of words known to be spelled
# correctly but that do not appear in the language dictionary selected by
# spelling_lang. The file should contain one word per line.
spelling_word_list_filename='spelling_wordlist.txt'
# Boolean controlling whether suggestions for misspelled words are printed.
# Defaults to False.
spelling_show_suggestions=True
# List of filter classes to be added to the tokenizer that produces words to be checked.
from checkin_filter import CheckinFilter
spelling_filters=[CheckinFilter]

View File

@@ -25,7 +25,7 @@ If you want to add a custom view to the control area of an event, just register
views.admin_view, name='backend'),
]
It is required that your URL parameters are called ``organizer`` and ``event``. If you want to
It is required that your URL paramaters are called ``organizer`` and ``event``. If you want to
install a view on organizer level, you can leave out the ``event``.
You can then implement the view as you would normally do. Our middleware will automatically

View File

@@ -11,7 +11,7 @@ Core
----
.. automodule:: pretix.base.signals
:members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types
:members: periodic_task, event_live_issues, event_copy_data
Order events
""""""""""""
@@ -47,7 +47,7 @@ Backend
-------
.. automodule:: pretix.control.signals
:members: nav_event, html_head, quota_detail_html, nav_topbar, nav_global, nav_organizer, nav_event_settings, order_info, event_settings_widget
:members: nav_event, html_head, quota_detail_html, nav_topbar, nav_global, nav_organizer, nav_event_settings, order_info
.. automodule:: pretix.base.signals

View File

@@ -1,5 +1,5 @@
Logging and notifications
=========================
Logging
=======
As pretix is handling monetary transactions, we are very careful to make it possible to review all changes
in the system that lead to the current state.
@@ -19,7 +19,7 @@ To actually log an action, you can just call the ``log_action`` method on your o
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
recomment do prefix it with your packagename, e.g. ``paypal.payment.rejected``. The ``user`` argument is
optional and may contain the user who performed the action. The optional ``data`` argument can contain
additional information about this action.
@@ -81,61 +81,6 @@ implementation could look like::
if logentry.action_type in plains:
return plains[logentry.action_type]
Sending notifications
---------------------
If you think that the logged information might be important or urgent enough to send out a notification to interested
organizers. In this case, you should listen for the :py:attr:`pretix.base.signals.register_notification_types` signal
to register a notification type::
@receiver(register_notification_types)
def register_my_notification_types(sender, **kwargs):
return [MyNotificationType(sender)]
Note that this event is different than other events send out by pretix: ``sender`` may be an event or ``None``. The
latter case is required to let the user define global notification preferences for all events.
You also need to implement a custom class that specifies how notifications should be handled for your notification type.
You should subclass the base ``NotificationType`` class and implement all its members:
.. autoclass:: pretix.base.notifications.NotificationType
:members: action_type, verbose_name, required_permission, build_notification
A simple implementation could look like this::
class MyNotificationType(NotificationType):
required_permission = "can_view_orders"
action_type = "pretix.event.order.paid"
verbose_name = _("Order has been paid")
def build_notification(self, logentry: LogEntry):
order = logentry.content_object
order_url = build_absolute_uri(
'control:event.order',
kwargs={
'organizer': logentry.event.organizer.slug,
'event': logentry.event.slug,
'code': order.code
}
)
n = Notification(
event=logentry.event,
title=_('Order {code} has been marked as paid').format(code=order.code),
url=order_url
)
n.add_attribute(_('Order code'), order.code)
n.add_action(_('View order details'), order_url)
return n
As you can see, the relevant code is in the ``build_notification`` method that is supposed to create a ``Notification``
method that has a title, description, URL, attributes, and actions. The full definition of ``Notification`` is the
following:
.. autoclass:: pretix.base.notifications.Notification
:members: add_action, add_attribute
Logging technical information
-----------------------------

View File

@@ -1,8 +1,6 @@
.. highlight:: python
:linenothreshold: 5
.. spelling:: answ contrib
Data model
==========
@@ -23,7 +21,7 @@ Organizers and events
:members:
.. autoclass:: pretix.base.models.Event
:members: get_date_from_display, get_time_from_display, get_date_to_display, get_date_range_display, presale_has_ended, presale_is_running, cache, lock, get_plugins, get_mail_backend, payment_term_last, get_payment_providers, get_invoice_renderers, active_subevents, invoice_renderer, settings
:members: get_date_from_display, get_time_from_display, get_date_to_display, get_date_range_display, presale_has_ended, presale_is_running, get_cache, lock, get_plugins, get_mail_backend, payment_term_last, get_payment_providers, get_invoice_renderers, active_subevents, invoice_renderer, settings
.. autoclass:: pretix.base.models.SubEvent
:members: get_date_from_display, get_time_from_display, get_date_to_display, get_date_range_display, presale_has_ended, presale_is_running

View File

@@ -35,7 +35,7 @@ Forms
-----
Hierarkey also provides a base class for forms that allow the modification of settings. pretix contains a
subclass that also adds support for internationalized fields:
subclass that also adds suport for internationalized fields:
.. autoclass:: pretix.base.forms.SettingsForm
@@ -65,4 +65,4 @@ Plugins can add custom hardcoded defaults in the following way::
Make sure that you include this code in a module that is imported at app loading time.
.. _django-hierarkey: https://github.com/raphaelm/django-hierarkey
.. _documentation: https://django-hierarkey.readthedocs.io/en/latest/
.. _documentation: https://django-hierarkey.readthedocs.io/en/latest/

View File

@@ -67,7 +67,7 @@ available as ``plugins:sendmail:send``.
Generating a URL for the frontend is a complicated task, because you need to know whether the event's
organizer uses a custom URL or not and then generate the URL with a different domain and different
arguments based on this information. pretix provides some helpers to make this easier. The first helper
is a python method that emulates a behavior similar to ``reverse``:
is a python method that emulates a behaviour similar to ``reverse``:
.. autofunction:: pretix.multidomain.urlreverse.eventreverse
@@ -82,5 +82,5 @@ Implementation details
----------------------
There are some other caveats when using a design like this, e.g. you have to care about cookie domains
and referrer verification yourself. If you want to see how we built this, look into the ``pretix/multidomain/``
and referer verification yourself. If you want to see how we built this, look into the ``pretix/multidomain/``
sub-tree.

View File

@@ -86,7 +86,7 @@ and head to http://localhost:8000/
As we did not implement an overall front page yet, you need to go directly to
http://localhost:8000/control/ for the admin view or, if you imported the test
data as suggested above, to the event page at http://localhost:8000/bigevents/2019/
data as suggested above, to the event page at http://localhost:8000/bigevents/2018/
.. note:: If you want the development server to listen on a different interface or
port (for example because you develop on `pretixdroid`_), you can check
@@ -106,7 +106,7 @@ Execute the following commands to check for code style errors::
isort -c -rc .
python manage.py check
Execute the following command to run pretix' test suite (might take a couple of minutes)::
Execute the following command to run pretix' test suite (might take a coumple of minutes)::
py.test
@@ -122,7 +122,7 @@ for example::
flake8 . || exit 1
isort -q -rc -c . || exit 1
This keeps you from accidentally creating commits violating the style guide.
This keeps you from accidentally creating commits violating the sdtyle guide.
Working with mails
^^^^^^^^^^^^^^^^^^

View File

@@ -1,6 +1,3 @@
.. spelling::
Analytics
List of plugins
===============

View File

@@ -157,7 +157,7 @@ uses to communicate with the pretix server.
.. http:get:: /pretixdroid/api/(organizer)/(event)/status/
Returns status information, such as the total number of tickets and the
number of performed check-ins.
number of performed checkins.
**Example request**:

View File

@@ -3,5 +3,3 @@ sphinx
sphinx-rtd-theme
sphinxcontrib-httpdomain
sphinxcontrib-images
sphinxcontrib-spelling
pyenchant

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -1,118 +0,0 @@
addon
addons
api
auth
autobuild
backend
backends
banktransfer
boolean
booleans
cancelled
casted
checkbox
checksum
config
contenttypes
contextmanager
cron
cronjob
debian
deduplication
discoverable
django
dockerfile
durations
eu
filename
filesystem
fontawesome
frontend
frontpage
gettext
gunicorn
hardcoded
hostname
invalidations
iterable
libsass
linters
memcached
metadata
middleware
mixin
mixins
multi
multidomain
namespace
namespaced
namespaces
namespacing
natively
nginx
NotificationType
ons
optimizations
param
percental
positionid
pre
prepend
prepended
prepending
preprocessor
presale
pretix
pretixdroid
pretixpresale
prometheus
proxied
proxying
queryset
redemptions
redis
refactored
regex
renderer
renderers
reportlab
screenshot
serializers
serializers
sexualized
startup
stdout
stylesheet
subdirectories
subdirectory
subdomain
subdomains
subevent
subevents
submodule
subpath
systemd
testutils
timestamp
un
unconfigured
unix
unprefixed
untrusted
username
url
viewset
viewsets
webhook
webhooks
webserver
webservice
workflow
zipcode
Datetime
Embeddable
Hierarkey
OAuth
SSL
Uptime
Yay

View File

@@ -1,5 +1,3 @@
.. _event_create:
Creating an event
=================
@@ -28,15 +26,15 @@ is useful if you have a large number of events that are very similar to each oth
(i.e. users should be able to buy tickets for multiple events at the same time). Those single events can differ in
available products, quotas, prices and some meta information, but most settings need to be the same for all of them.
We recommend to use this feature only if you really know that you need it and if you really run a lot of events, not if
you run e.g. a yearly conference. You can read more on this feature :ref:`here <subevents>`.
you run e.g. a yearly conference.
Once you set these values, you can proceed to the next step:
Once you set these values, you can procede to the next step:
.. thumbnail:: ../../screens/event/create_step2.png
:align: center
:class: screenshot
In this step, you will be asked more detailed questions about your event. In particular, you can fill in the
In this step, you will be asked more detailled questions about your event. In particular, you can fill in the
following fields:
Name

View File

@@ -1,42 +0,0 @@
Display settings
================
The settings at "Settings" → "Display" allow you to customize the appearance of your ticket shop.
.. thumbnail:: ../../screens/event/settings_display.png
:align: center
:class: screenshot
The upper part of the page contains settings that you always need to set specifically for your event. Those are
currently::
Logo image
This logo will be shown as a banner above your shop. If you set it, the event name and date will no longer be
displayed by the shop, so we suggest to include them in the image yourself. The maximal height of the image is
120 pixels and if you want to use the full width, make your image 1140 pixels wide. If the user's screen is
smaller, the logo will be scaled down automatically, so it should still be legible at smaller sizes.
Frontpage text
This text will be shown on the front page of your ticket shop, above the list of products. You can use it to explain
your product types, give more information on the event or for other general notices.
You can use :ref:`Markdown syntax <markdown-guide>` in this field.
Show variations of a product expanded by default
If this is not checked, a product with variations will be shown as one row in the show by default and will expand
into multiple rows once it is clicked on. With this box checked, the variations will be shown as multiple rows
right from the beginning.
The lower part of the page contains settings that you can **either** set on organizer-level for all your events **or**
override for this single event individually. Those are:
Primary color
This color will be used for links, buttons, and other design elements throughout your shop and emails sent to your
customers. We suggest not choosing something to light, since text in that color should be readable on a white
background and white text should be readable on a background of this color.
Font
Choose one of multiple fonts to use for your web shop.
.. note:: Both the color and font settings can take a few seconds up to a few minutes before they become active on your
shop.

View File

@@ -1,129 +0,0 @@
E-mail settings
===============
The settings at "Settings" → "E-mail" allow you to customize the emails that pretix sends to the participants of your
event.
.. thumbnail:: ../../screens/event/settings_email.png
:align: center
:class: screenshot
The page is separated into three parts: "E-mail settings", "E-mail content" and "SMTP settings". We will explain all
of them in detail on this page.
E-mail settings
---------------
The upper part of the page contains settings that are relevant for the generation of all e-mails alike. Those are
currently::
Subject prefix
This text will be prepended to the subject of all e-mails that are related to your event. For example, if you
set this to "dc2018" all subjects will be formatted like "[dc2018] Your payment was successful".
Sender address
All e-mails will be sent with this address in the "From" field. If you use an email address at a custom domain,
we strongly recommend to use the SMTP settings below as well, otherwise your e-mails might be detected as spam
due to the `Sender Policy Framework`_ and similar mechanisms.
Signature
This text will be appended to all e-mails in form of a signature. This might be useful e.g. to add your contact
details or any legal information that needs to be included with the e-mails.
E-mail content
--------------
The middle part of the page allows you to customize the exact texts of all e-mails sent by the system automatically.
You can click on the different boxes to expand them and see the texts.
Within the texts, you can use placeholders that will later by replaced by values depending on the event or order. Below
every text box is a list of supported placeholders, but currently the following are defined (not every placeholder
is valid in every text):
============================== ===============================================================================
Placeholder Description
============================== ===============================================================================
event The event name
total The order's total value
currency The currency used for the event (three-letter code)
payment_info Information text specific to the payment method (e.g. banking details)
url An URL pointing to the download/status page of the order
invoice_name The name field of the invoice address
invoice_company The company field of the invoice address
expire_date The order's expiration date
date The same as ``expire_date``, but in a different e-mail (for backwards
compatibility)
orders A list of orders including links to their status pages, specific to the "resend
link (requested by user)" e-mail
code In case of the waiting list, the voucher code to redeem
hours In case of the waiting list, the number of hours the voucher code is valid
============================== ===============================================================================
The different e-mails are explained in the following:
Placed Order
This e-mail is sent out to every order directly after the order has been received, except if the order total
is zero (see below). It should specify that/how the order is to be paid.
Paid Order
This e-mail is sent out as soon as the payment for an order has been received and should give the customer
more information on how to proceed, e.g. by downloading their ticket.
Free Order
This e-mail is sent out instead of "Placed Order" and "Paid Order" if the order total is zero. It therefore should
tell the same information, except asking the customer for completing their payment.
Resend link
Sent by admin
This e-mail will be sent out if you click the "Resend link" next to the e-mail address field on the order detail
page. It should include the link to the order and can be sent to users e.g. if they lost their original e-mails.
Requested by user
Customers can also request a link to all orders they created using their e-mail address themselves by filling
out a form on the website. In this case, they will receive an e-mail containing a list of all orders they created
with the respective links.
Order changed
This e-mail is sent out if you change the content of the order and choose to notify the user about it.
Payment reminder
This e-mail is sent out a certain number of days before the order's expiry date. You can specify the number of days
before the expiry date that this should happen and the e-mail will only ever be sent if you do specify such a
number. The text should ask the customer to complete the payment, tell the options on how to do so and the
consequences if no payment is received (ticket gone, depending on your other settings). You should also include
a way to contact you in case of questions.
Waiting list notification
If you enable the waiting list feature, this is the mail that will be sent out if a ticket is assigned to a person on
the waiting list. It should include the voucher that needs to be redeemed to get the free spot and tell how long
that voucher is valid and where to redeem it.
Order canceled
This e-mail is sent to a customer if their order has been canceled.
Order custom mail
You can use pretix' admin interface to directly send an e-mail with a custom text to the customer of a specific
order. In this case, this will be the default text and might save you time by not having to re-type all of it every
time.
Reminder to download tickets
If you want, you can configure an email that will be send out a number of days before your event to remind
attendees to download their tickets. The e-mail should include a link to the ticket download. This e-mail will only
ever be sent if you specify a number of days.
SMTP settings
-------------
If you want to send your e-mails via your own e-mail address, we strongly recommend to use SMTP for this purpose.
SMTP is a protocol that is used by e-mail clients to communicate with e-mail servers. Using SMTP, pretix can talk to
your e-mail service provider the same way that e.g. the e-mail app on your phone can.
Your e-mail provider will most likely have a document that tells you the settings for the various fields to fill in
here (hostname, port, username, password, encryption).
With the checkbox "Use custom SMTP server" you can turn using your SMTP server on or off completely. With the
button "Save and test custom SMTP connection", you can test if the connection and authentication to your SMTP server
succeeds, even before turning that checkbox on.
.. _Sender Policy Framework: https://en.wikipedia.org/wiki/Sender_Policy_Framework

View File

@@ -1,96 +0,0 @@
Invoice settings
================
.. spelling:: Inv
The settings at "Settings" → "Invoice" allow you to specify if and how pretix should generate invoices for your orders.
.. thumbnail:: ../../screens/event/settings_invoice.png
:align: center
:class: screenshot
In particular, you can configure the following things:
Ask for invoice address
If this checkbox is enabled, customers will be able to enter an invoice address during checkout. If you only enable
this box, the invoice address will be optional to fill in.
Require invoice address
If this checkbox is enabled, entering an invoice address will be obligatory for all customers and it will not be
able to create an order without entering an address.
Require customer name
If this checkbox is enabled, the street, city, and country fields of the invoice address will still be optional but
the name field will be obligatory.
Generate invoices
This field controls whether pretix should generate an invoice for an order. You have the following options:
No
pretix will never generate an invoice. If you want to issue invoices, you need to do it yourself based on the
collected address data.
Manually in admin panel
pretix will not create invoices automatically, but the order detail view will show a button that allows you to
manually generate one for specific orders.
Automatically on user request
pretix will not create invoices on its own, but both the panel as well as the customer view of the order will
show a button that instantly generates an invoice for the specified order.
Automatically for all created orders
pretix will automatically create an invoice every time an order is placed.
Automatically on payment
pretix will automatically create an invoice for an order, as soon as the payment for the order is received.
pretix will never generate invoices for free orders, even though it might ask for the invoice address.
Attach invoices to emails
If enabled, invoices will be attached to order confirmation e-mails if the "Generate invoices" setting is set to
"Automatically for all created orders" or to the payment confirmation e-mails if it is set to "Automatically on
payment".
Ask for VAT ID
If enabled, the invoice address form will not only ask for a postal address, but also for a VAT ID. The VAT ID will
always be an optional field.
Generate invoices with consecutive numbers
If enabled, invoices will be created with numerical invoice numbers in the order of their creation, i.e.
PREFIX-00001, PREFIX-00002, and so on. If disabled, invoice numbers will instead be generated from the order code,
i.e. PREFIX-YHASD-1. When in doubt, keep this option enabled since it might be legally required in your country,
but disabling it has the advantage that your customers can not estimate the number of tickets sold by looking at
the invoice numbers.
Invoice number prefix
This is the prefix that will be prepended to all your invoice numbers. For example, if you set this to "Inv", your
invoices will be numbered Inv00001, Inv00002, etc. If you leave this field empty, your event slug will be used,
followed by a dash, e.g. DEMOCON-00001.
Within one organizer account, events with the same number prefix will share their number range. For example, if you
set this to "Inv" for all of your events, there will be only one invoice numbered Inv00007 across all your events
and the numbers will have gaps within one event.
Show free products on invoices
If enabled, products that do not cost anything will still show up on invoices. Note that the order needs to contain
at least one non-free product in order to generate an invoice.
Show attendee names on invoices
If enabled, the attendee name will be printed on the invoice for admission tickets.
Your address
This should be set to the address of the entity issuing the invoice (read: you) and will be printed inside
the header of the invoice.
Introductory text
A free custom text that will be printed above the list of products on the invoice.
Additional text
A free custom text that will be printed below the list of products and the invoice total.
Footer
A text that will be printed in the foot line of the invoice. This could contain your contact details or legal
information on the issuing entity, e.g. registration numbers, your VAT ID, etc.
Logo image
A square image that will be printed in the invoice header, currently with a width of 2.5cm.

View File

@@ -1,20 +0,0 @@
Configuring plugins
===================
Plugins are optional parts of pretix that can be installed to extend the available functionality and that can be turned
on or off completely for every event. For your event, a number of plugins might be active already, but you can unlock
even more functionality by going to "Settings" → "Plugins" and enable more of them, if you need.
.. thumbnail:: ../../screens/event/settings_plugins.png
:align: center
:class: screenshot
For each plugin, you will find a short description as well as an Enable/Disable button. The pretix website has
`an overview`_ of available plugins and more details of them. If you are on the pretix.eu hosted service, look for
the "pretix Hosted" badge in the plugin list to learn which ones are supported there.
If you are running pretix on your own server, refer to the installation manual of your installation type to learn
how to install additional plugins (:ref:`manual <manual_plugininstall>` or :ref:`Docker <docker_plugininstall>`).
.. _an overview: https://pretix.eu/about/en/plugins

View File

@@ -1,14 +0,0 @@
Configuring an event
====================
.. toctree::
:maxdepth: 2
subevents
../payments/index
plugins
display
tickets
email
taxes
invoicing

View File

@@ -1,111 +0,0 @@
.. _subevents:
Event series
============
During creation of a new event, you can choose that you want to create this event as an event series.
By event series, we mean a group of events that are similar in their structure and that you want to
sell within a single shop. An event series consists of **dates**. Each date represents one "event"
within the series.
For example, we think good examples to use the event series feature are:
* A theater or theater group that shows the same play on five evenings.
* A band on tour that hosts the same show in different locations.
* A workshop that is given multiple times in different locations or at different times.
We **don't** think that the feature is well-suited for events like the following:
* Event series distributed over a large timescale like annual conferences. We suggest using multiple events in this
case. You can avoid having to configure everything twice since you can copy settings from an existing event during
creation of the new event.
* Multiple parts of a conference or festival (e.g. different days) if a significant number of attendees will visit
more than one of them. We suggest just using different products in this case.
When using an event series, the single dates of the series are using the same settings in most places. They can
**only** differ in the following aspects:
* They can have different date, time, and location parameters.
* They can use different text on the shop front page.
* They can have different prices for the various products.
* They always have distinct quotas, which allows you to assign different amounts of tickets or to enable or disable
some products completely.
* They can have different rules for check-in.
Therefore, if your events are likely to need more different settings, this is probably not the feature for you. The
benefits of using event series, on the other hand, are:
* You only need to set most settings once, as the multiple dates live in the same shop.
* Your customers can build mixed orders, i.e. they can order tickets for multiple dates at once.
Creating and modifying dates in the series
------------------------------------------
Click on "Dates" in the left navigation menu of your event. This page shows you the list of currently existing event
dates and allows you to create, edit, clone and delete them.
If "Dates" is missing from the navigation menu, you have insufficient permission or your event has not been set up as
an event series and you need to create a new event.
.. thumbnail:: ../../screens/event/subevent_list.png
:align: center
:class: screenshot
If you click on one of them or create a new one, you will see the following form:
.. thumbnail:: ../../screens/event/subevent_create.png
:align: center
:class: screenshot
Here, you can make changes to the following fields, most of which are optional:
Name
This is the public name of your date. It should be descriptive enough to tell the user which date to select in
a calendar.
Active
This date will only show up for customers if you check this box. In this sense, it corresponds to the "live" setting
of events.
Event start time
The date and time that this date starts at.
Event end time
The date and time this date ends at.
Location
This is the location of your date in a human-readable format. We will show this on the ticket shop frontpage, but
it might also be used e.g. in Wallet tickets.
Admission time
The admission date and time to show on the ticket shop page or on the tickets.
Frontpage text
A text to show on the front page of the ticket shop for this date.
Start of presale
If you set this, no ticket will be sold before the time you set. If you set this on event series level as well,
both dates must be in the past for the tickets to be available.
End of presale
If you set this, no ticket will be sold after the time you set. If you set this on event series level as well,
both dates must be in the future for the tickets to be available.
Quotas
As for all events, no tickets will be available unless there is a quota created for them that specifies the number
of tickets available. You can create multiple quotas that are assigned to this date directly from this interface.
Item prices
This is a table of all products configured for your shop. If you want, you can enter a new price for each one of them
in the right column to make them cheaper or more expensive for this date. If you leave a field empty, the price will
follow the product's default price.

View File

@@ -1,7 +1,5 @@
.. _taxes:
Configuring taxes
=================
Tax rules
=========
In most countries, you will be required to pay some form of sales tax for your event tickets. If you don't know about
the exact rules, you should consult a professional tax consultant right now.
@@ -18,7 +16,7 @@ your event, go to the respective section in your event's settings:
:class: screenshot
On this page, you can create, edit and delete your tax rules. Clicking on the name of a tax rule will take you to its
detailed settings:
detailled settings:
.. thumbnail:: ../../screens/event/tax_detail.png
:align: center

View File

@@ -1,30 +0,0 @@
Ticket settings
===============
At "Settings" → "Tickets", you can configure the ticket download options that will be presented to your customers:
.. thumbnail:: ../../screens/event/settings_tickets.png
:align: center
:class: screenshot
The top of this page shows a short list of options relevant for all download formats:
Use feature
This can be used to completely enable or disable ticket downloads all over your ticket shop.
Download date
If you set a date here, no ticket download will be offered before this date. If no date is set, tickets can be
downloaded immediately after the payment for an order has been received.
Offer to download tickets separately for add-on products
By default, tickets can not be downloaded for order positions which are only an add-on to other order positions. If
you enable this, this behavior will be changed and add-on products will get their own tickets as well. If disabled,
you can still print a list of chosen add-ons e.g. on the PDF tickets.
Generate tickets for non-admission products
By default, tickets will only be generated for products that are marked as admission products. Enable this option to
generate tickets for all products instead.
Below these settings, the detail settings for the various ticket file formats are offered. They differ from format to
format and only share the common "Enable" setting that can be used to turn them on. By default, pretix ships with
a PDF output plugin that you can configure through a visual design editor.

View File

@@ -1,104 +0,0 @@
Embeddable Widget
=================
If you want to show your ticket shop on your event website or blog, you can use our JavaScript widget. This way,
users will not need to leave your site to buy their ticket in most cases. The widget will still open a new tab
for the checkout if the user is on a mobile device.
To obtain the correct HTML code for embedding your event into your website, we recommend that you go to the "Widget"
tab of your event's settings. You can specify some optional settings there (for example the language of the widget)
and then click "Generate widget code".
.. thumbnail:: ../../screens/event/widget_form.png
:align: center
:class: screenshot
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">
<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::
<pretix-widget event="https://pretix.eu/demo/democon/"></pretix-widget>
<noscript>
<div class="pretix-widget">
<div class="pretix-widget-info-message">
JavaScript is disabled in your browser. To access our ticket shop without JavaScript,
please <a target="_blank" href="https://pretix.eu/demo/democon/">click here</a>.
</div>
</div>
</noscript>
.. note::
You can of course embed multiple widgets of multiple events on your page. In this case, please add the first
snippet only *once* and the second snippets once *for each event*.
Example
-------
Your embedded widget could look like the following:
.. raw:: html
<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>
<pretix-widget event="https://pretix.eu/demo/democon/"></pretix-widget>
<noscript>
<div class="pretix-widget">
<div class="pretix-widget-info-message">
JavaScript is disabled in your browser. To access our ticket shop without javascript, please <a target="_blank" href="https://pretix.eu/demo/democon/">click here</a>.
</div>
</div>
</noscript>
Styling
-------
If you want, you can customize the appearance of the widget to fit your website with CSS. If you inspect the rendered
HTML of the widget with your browser's developer tools, you will see that nearly every element has a custom class
and all classes are prefixed with ``pretix-widget``. You can override the styles as much as you want to and if
you want to go all custom, you don't even need to use the stylesheet provided by us at all.
SSL
---
Since buying a ticket normally involves entering sensitive data, we strongly suggest that you use SSL/HTTPS for the page
that includes the widget. Initiatives like `Let's Encrypt`_ allow you to obtain a SSL certificate free of charge.
All data transferred to pretix will be made over SSL, even if using the widget on a non-SSL site. However, without
using SSL for your site, a man-in-the-middle attacker could potentially alter the widget in dangerous ways. Moreover,
using SSL is becoming standard practice and your customers might want expect see the secure lock icon in their browser
granted to SSL-enabled web pages.
By default, the checkout process will open in a new tab in your customer's browsers if you don't use SSL for your
website. If you confident to have a good reason for not using SSL, you can override this behavior with the
``skip-ssl-check`` attribute::
<pretix-widget event="https://pretix.eu/demo/democon/" skip-ssl-check></pretix-widget>
Pre-selecting a voucher
-----------------------
You can pre-select a voucher for the widget with the ``voucher`` attribute::
<pretix-widget event="https://pretix.eu/demo/democon/" voucher="ABCDE123456"></pretix-widget>
This way, the widget will only show products that can be bought with the voucher and prices according to the
voucher's settings.
.. raw:: html
<pretix-widget event="https://pretix.eu/demo/democon/" voucher="ABCDE123456"></pretix-widget>
<noscript>
<div class="pretix-widget">
<div class="pretix-widget-info-message">
JavaScript is disabled in your browser. To access our ticket shop without javascript, please <a target="_blank" href="https://pretix.eu/demo/democon/">click here</a>.
</div>
</div>
</noscript>
.. _Let's Encrypt: https://letsencrypt.org/

View File

@@ -1,51 +0,0 @@
FAQ and Troubleshooting
=======================
How can I test my shop before taking it live?
---------------------------------------------
There are multiple ways to do this.
First, you could just create some orders in your real shop and cancel/refund them later. If you don't want to process
real payments for the tests, you can either use a "manual" payment method like bank transfer and just mark the orders
as paid with the button in the backend, or if you want to use e.g. Stripe, you can configure pretix to use your keys
for the Stripe test system and use their test credit cars. Read our :ref:`Stripe documentation <stripe>` for more
information.
Second, you could create a separate event, just for testing. In the last step of the :ref:`event creation process <event_create>`,
you can specify that you want to copy all settings from your real event, so you don't have to do all of it twice.
We are planning to add a dedicated test mode in a later version of pretix.
If you are using the hosted service at pretix.eu and want to get rid of the test orders completely, contact us at
support@pretix.eu and we can remove them for you. Please note that we only are able to do that *before* you have
received any real orders (i.e. taken the shop public). We won't charge any fees for test orders or test events.
How do I delete an event?
-------------------------
It is currently not possible to delete events, you can just disable the shop by clicking the first square on your event
dashboard. Events can't be deleted as they most likely contain information on financial transactions which legally
needs to be kept on record for multiple years in most countries.
If you are using the hosted service at pretix.eu and want to get rid of an event that you only used for testing, contact
us at support@pretix.eu and we can remove it for you.
Why doesn't my product show up in the ticket shop?
--------------------------------------------------
If you created a product and it doesn't show up, please follow the following steps to find out why:
1. Check if the product's "active" checkbox is enabled.
2. Check if the product is in a category that has the "Products in this category are add-on products" checkbox enabled.
If this is the case, the product won't show up on the shop front page, but only in the first step of checkout when
a product in the cart allows to add add-on products from this category.
3. Check if the product's "Available from" or "Available until" settings restrict it to a date range.
4. Check if the product's checkbox "This product will only be shown if a voucher matching the product is redeemed." is
enabled. If this is the case, the product will only be shown if the customer redeems a voucher that *directly* matches
to this product. It will not be shown if the voucher only is configured to match a quota that contains the product.
5. Check that a quota exists that contains this product. If your product has variations, check that at least one
variation is contained in a quota. If your event is an event series, make sure that the product is contained in a
quota that is assigned to the series date that you access the shop for.
6. If the sale period has not started yet or is already over, check the "Show items outside presale period" setting of
your event.

View File

@@ -9,7 +9,5 @@ wanting to use pretix to sell tickets.
organizers/index
events/create
events/settings
events/widget
faq
markdown
events/taxes
payments/index

View File

@@ -1,166 +0,0 @@
.. _markdown-guide:
Markdown Guide
==============
What is markdown?
-----------------
In many places of your shop, like frontpage texts, product descriptions and email texts, you can use
`Markdown`_ to create links, bold text, and other formatted content. Markdown is a good middle-ground
since it is way easier to learn than languages like HTML but allows all basic formatting options required
for text in those places.
Formatting rules
----------------
Simple text formatting
""""""""""""""""""""""
To set a text in italics, you can put it in asterisks or underscores. For example,
.. code-block:: markdown
Please *really* pay your _ticket_.
will become:
Please *really* pay your _ticket_.
If you set double asterisks or underscores, the text will be printed in bold. For example,
.. code-block:: markdown
This is **important**.
will become:
This is **important**.
You can also display, for example:
.. code-block:: markdown
Input this `exactly like this`.
You will get:
Input this ``exactly like this``.
Links
"""""
You can create a link by just pasting it in, e.g.
.. code-block:: markdown
Check this on https://en.wikipedia.org
will become:
Check this on https://en.wikipedia.org
However, if you want to control the text of the link, you can put the text of the link in ``[]`` brackets and the
link target in ``()`` parentheses, like this:
.. code-block:: markdown
Check this on [Wikipedia](https://en.wikipedia.org).
This will yield:
Check this on `Wikipedia`_
All links created with pretix Markdown syntax will open in a new tab.
Lists
"""""
You can create un-numbered lists by prepending the lines with asterisks.
.. code-block:: markdown
* First item
* Second item with a text that is too long to
fit in a line
* Third item
will become:
* First item
* Second item with a text that is too long to
fit in a line
* Third item
You can also use numbers as list items
.. code-block:: markdown
1. Red
2. Green
3. Blue
to get
1. Red
2. Green
3. Blue
Headlines
"""""""""
To create a headline, prepend it with ``#`` for the main headline, ``##`` for a headline of the second level,
and so on. For example:
.. code-block:: markdown
# Headline 1
## Headline 2
### Headline 3
#### Headline 4
##### Headline 5
###### Headline 6
We do not recommend using headlines of the first level, as pretix will already set the name of your event as a level-1
headline of the page and HTML pages should have only one headline on the first level.
You can also use
.. code-block:: markdown
*****
to create a horizontal line, like the following:
.. raw:: html
<hr>
Using HTML
----------
You can also directly embed HTML code, if you want, although we recommend
using Markdown, as it enables e.g. people using text-based email clients
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, span, strong,
table, tbody, td, thead, tr, ul
Additionally, only the following attributes are allowed on them::
<a href="…" title="…">
<abbr title="…">
<acronym title="…">
<table width="…">
<td width="…" align="…">
<div class="…">
<p class="…">
<span class="…">
All other elements and attributes will be stripped during parsing.
.. _Markdown: https://en.wikipedia.org/wiki/Markdown
.. _Wikipedia: https://en.wikipedia.org

View File

@@ -1,43 +0,0 @@
Organizer account
=================
The basis of all your operations within pretix is your organizer account. It represents an entity that is running
events, for example a company, yourself or any other institution.
Every event belongs to one organizer account and events within the same organizer account are assumed to belong together
in some sense, whereas events in different organizer accounts are completely isolated.
If you want to use the hosted pretix service, you can create an organizer account on our `Get started`_ page. Otherwise,
ask your pretix administrator for access to an organizer account.
You can find out all organizer accounts you have access to by going to your global dashboard (click on the pretix logo
in the top-left corner) and then select "Organizers" from the navigation bar on the left side. Then, choose one of the
organizer accounts presented, if there are multiple of them:
.. thumbnail:: ../../screens/organizer/list.png
:align: center
:class: screenshot
This overview shows you all event that belong to the organizer and you have access to:
.. thumbnail:: ../../screens/organizer/event_list.png
:align: center
:class: screenshot
With the "Edit" button at the top, next to the organizer account name, you can modify properties of the organizer
account such as its name and display settings for the public profile page of the organizer account:
.. thumbnail:: ../../screens/organizer/edit.png
:align: center
:class: screenshot
.. tip::
The profile page will be shown as ``https://pretix.eu/slug/`` where ``slug`` is to be replaced by the short form of
the organizer name that you entered during account creation and ``pretix.eu`` is to be replaced by your
installation's domain name if you are not using our hosted service.
Instead, you can also use a custom domain for the profile page and your events, for example
``https://tickets.example.com/`` if ``example.com`` is a domain that you own. Head to :ref:`custom_domain` to learn
more.
.. _Get started: https://pretix.eu/about/en/setup

View File

@@ -1,54 +0,0 @@
.. _custom_domain:
Using a custom domain
=====================
By default, event shops built with pretix are accessible at ``https://<domain>/<organizer>/<event>/``, where
``<domain>`` is ``pretix.eu`` if you are using our hosted service and ``<organizer>`` and ``<event>`` are the short
form versions of your organizer account name and event name, respectively.
However, you are also able to use a custom domain for your ticket shops! If you work for "Awesome Party Corporation"
and your website is ``awesomepartycorp.com``, you might want to sell your tickets at ``tickets.awesomepartycorp.com``
and with pretix, you can do this. On this page, you find out the necessary steps to take.
With the pretix.eu hosted service
---------------------------------
Step 1: DNS Configuration
#########################
Go to the website of the provider you registered your domain name with. Look for the "DNS" settings page in their
interface. Unfortunately, we can't tell you exactly how that is named and how it looks, since it is different for every
domain provider.
Use this interface to add a new subdomain record, e.g. ``tickets`` of the type ``CNAME`` (might also be called "alias").
The value of the record should be ``www.pretix.eu``.
Step 2: Wait for the DNS entry to propagate
###########################################
Submit your changes and wait a bit, it can regularly take up to three hours for DNS changes to propagate to the caches
of all DNS servers. You can try checking by accessing your new subdomain, ``http://tickets.awesomepartycorp.com``.
If DNS was changed successfully, you should see a SSL certificate error. If you ignore the error and access the page
anyways, you should get a pretix-themed error page with the headline "Unknown domain".
Step 3: Tell us
###############
Write an email to support@pretix.eu, naming your new domain and your organizer account. We will then generate a SSL
certificate for you (for free!) and configure the domain.
With a custom pretix installation
---------------------------------
If you installed pretix on a server yourself, you can also use separate domains for separate organizers.
First of all, configure your webserver or reverse proxy to pass requests to the new domain to pretix as well.
Then, go to the organizer account in pretix and click the "Edit" button. Enter the new domain in the "Custom Domain"
field, then you're done!
.. thumbnail:: ../../screens/organizer/edit_sysadmin.png
:align: center
:class: screenshot
Note that this field only shows up if you are logged in as a system administrator of your pretix installation.

View File

@@ -1,9 +1,112 @@
Organizer accounts and teams
============================
.. toctree::
:maxdepth: 2
Organizer account
-----------------
account
teams
domain
The basis of all your operations within pretix is your organizer account. It represents an entity that is running
events, for example a company, yourself or any other institution.
Every event belongs to one organizer account and events within the same organizer account are assumed to belong together
in some sense, whereas events in different organizer accounts are completely isolated.
If you want to use the hosted pretix service, you can create an organizer account on our `Get started`_ page. Otherwise,
ask your pretix administrator for access to an organizer account.
You can find out all organizer accounts you have access to by going to your global dashboard (click on the pretix logo
in the top-left corner) and then select "Organizers" from the navigation bar on the left side. Then, choose one of the
organizer accounts presented, if there are multiple of them:
.. thumbnail:: ../../screens/organizer/list.png
:align: center
:class: screenshot
This overview shows you all event that belong to the organizer and you have access to:
.. thumbnail:: ../../screens/organizer/event_list.png
:align: center
:class: screenshot
With the "Edit" button at the top, next to the organizer account name, you can modify properties of the organizer
account such as its name and display settings for the public profile page of the organizer account:
.. thumbnail:: ../../screens/organizer/edit.png
:align: center
:class: screenshot
.. tip::
The profile page will be shown as ``https://pretix.eu/slug/`` where ``slug`` is to be replaced by the short form of
the organizer name that you entered during account creation and ``pretix.eu`` is to be replaced by your
installation's domain name if you are not using our hosted service.
Instead, you can also use a custom domain for the profile page and your events, for example
``https://tickets.example.com/`` if ``example.com`` is a domain that you own. In this case, please contact the pretix
hosted support or your system administrator to set up the custom domain.
Teams
-----
We don't expect you to work on your events all by yourself and therefore, pretix comes with ways to invite your fellow
team members to access your pretix organizer account. To manage teams, click on the "Teams" link on your organizer
settings page (see above how to find it). This shows you a list of teams that should contain at least one team already:
.. thumbnail:: ../../screens/organizer/team_list.png
:align: center
:class: screenshot
If you click on a team name, you get to a page that shows you the current members of the team:
.. thumbnail:: ../../screens/organizer/team_detail.png
:align: center
:class: screenshot
You see that there is a list of pretix user accounts (i.e. email addresses), who are part of the team. To add a user to
the team, just enter their email address in the text box next to the "Add" button. If the user already has an account
in the pretix system they will instantly get access to the team. Otherwise, they will be sent an email with an invitation
link that can be used to create an account. This account will then instantly have access to the team. Users can be part
of as many teams as you want.
In the section below, you can also create access tokens for our :ref:`rest-api`. You can read more on this topic in the
section :ref:`rest-auth` of the API documentation.
Next to the team name, you again see a button called "Edit" that allows you to modify the permissions of the team.
Permissions separate into two areas:
* **Organizer permissions** allow actions on the level of an organizer account, in particular:
* Can create events To create a new event under this organizer account, users need to have this permission
* Can change teams and permissions This permission is required to perform the kind of action you are doing right now.
Anyone with this permission can assign arbitrary other permissions to themselves, so this is the most powerful
permission there is to give.
* Can change organizer settings This permission is required to perform changes to the settings of the organizer
account, e.g. its name or display settings.
* **Event permissions** allow actions on the level of an event. You can give the team access to all events of the
organizer (including future ones that are not yet created) or just a selected set of events. The specific permissions to choose from are:
* Can change event settings This permission gives access to most areas of the control panel that are not controlled
by one of the other event permissions, especially those that are related to setting up and configuring the event.
* Can change product settings This permission allows to create and modify products and objects that are closely
related to products, such as product categories, quotas, and questions.
* Can view orders This permission allows viewing the list of orders and allindividual order details, but not
changing anything about it. This also includes the various exports offered.
* Can change orders This permission allows all actions that involve changing an order, such as changing the products
in an order, marking an order as paid or refunden, importing banking data, etc. This only works properly if the
same users also have the "Can view orders" permission.
* Can view vouchers This permission allows viewing the list of vouchers including the voucher codes themselves and
their redemption status.
* Can change vouchers This permission allows to create and modify vouchers in all their details. It only works
properly if the same users also have the "Can view vouchers" permission.
.. thumbnail:: ../../screens/organizer/team_edit.png
:align: center
:class: screenshot
.. _Get started: https://pretix.eu/about/en/setup

View File

@@ -1,65 +0,0 @@
Teams
=====
We don't expect you to work on your events all by yourself and therefore, pretix comes with ways to invite your fellow
team members to access your pretix organizer account. To manage teams, click on the "Teams" link on your organizer
settings page (see above how to find it). This shows you a list of teams that should contain at least one team already:
.. thumbnail:: ../../screens/organizer/team_list.png
:align: center
:class: screenshot
If you click on a team name, you get to a page that shows you the current members of the team:
.. thumbnail:: ../../screens/organizer/team_detail.png
:align: center
:class: screenshot
You see that there is a list of pretix user accounts (i.e. email addresses), who are part of the team. To add a user to
the team, just enter their email address in the text box next to the "Add" button. If the user already has an account
in the pretix system they will instantly get access to the team. Otherwise, they will be sent an email with an invitation
link that can be used to create an account. This account will then instantly have access to the team. Users can be part
of as many teams as you want.
In the section below, you can also create access tokens for our :ref:`rest-api`. You can read more on this topic in the
section :ref:`rest-auth` of the API documentation.
Next to the team name, you again see a button called "Edit" that allows you to modify the permissions of the team.
Permissions separate into two areas:
* **Organizer permissions** allow actions on the level of an organizer account, in particular:
* Can create events To create a new event under this organizer account, users need to have this permission
* Can change teams and permissions This permission is required to perform the kind of action you are doing right now.
Anyone with this permission can assign arbitrary other permissions to themselves, so this is the most powerful
permission there is to give.
* Can change organizer settings This permission is required to perform changes to the settings of the organizer
account, e.g. its name or display settings.
* **Event permissions** allow actions on the level of an event. You can give the team access to all events of the
organizer (including future ones that are not yet created) or just a selected set of events. The specific permissions to choose from are:
* Can change event settings This permission gives access to most areas of the control panel that are not controlled
by one of the other event permissions, especially those that are related to setting up and configuring the event.
* Can change product settings This permission allows to create and modify products and objects that are closely
related to products, such as product categories, quotas, and questions.
* Can view orders This permission allows viewing the list of orders and all individual order details, but not
changing anything about it. This also includes the various exports offered.
* Can change orders This permission allows all actions that involve changing an order, such as changing the products
in an order, marking an order as paid or refunded, importing banking data, etc. This only works properly if the
same users also have the "Can view orders" permission.
* Can view vouchers This permission allows viewing the list of vouchers including the voucher codes themselves and
their redemption status.
* Can change vouchers This permission allows to create and modify vouchers in all their details. It only works
properly if the same users also have the "Can view vouchers" permission.
.. thumbnail:: ../../screens/organizer/team_edit.png
:align: center
:class: screenshot

View File

@@ -30,9 +30,3 @@ many orders could be processed correctly and how many could not. You can then go
transfers from your bank statement that are not yet matched to an order. Using the input field and the buttons on the
left of each transaction, you can manually enter an order code to match it to or just discard it from the list, e.g.
if the transaction is not related to the event at all.
.. tip:: If you aren't afraid of getting a bit more technical and your bank supports the HBCI/FinTS protocol (as most
German banks do), you can use `pretix-banktool`_ to fully automate this process.
.. _pretix-banktool: https://github.com/pretix/pretix-banktool

View File

@@ -1,5 +1,3 @@
.. _payment-fees:
Payment method fees
===================
@@ -20,9 +18,6 @@ might also decide to go for option one to make it easier for customers who don't
legislation might already be in place or become relevant from January 2018 the latest. This is not
legal advice. If in doubt, consult a lawyer or refrain from charging payment fees.
If you go for the first option (as you should in the EU), you can just leave the payment fee fields in pretix' settings
empty.
If you go for the second option, you can configure pretix to charge the payment method fees to your user. You can
define both an absolute fee as well as a percental fee based on the order total. If you do so, there are two
different ways in which pretix can calculate the fee. Normally, it is fine to just go with the default setting, but
@@ -60,4 +55,4 @@ same 5 %, such that for a ticket with a list price of 100 € you will get your
===================================================== =============
Due to the various rounding steps performed by pretix and by the payment provider, the end total on
your bank account might still vary by one cent.
your bank account might stil vary by one cent.

View File

@@ -1,10 +1,9 @@
Payment settings
================
Accepting payments
==================
.. toctree::
:maxdepth: 2
settings
overview
fees
paypal

View File

@@ -2,7 +2,7 @@ Payment method overview
=======================
pretix allows you to accept payments using a variety of payment methods to fit the needs of very different events.
This page gives you a short overview over them and links to more detailed descriptions in some cases.
This page gives you a short overview over them and links to more detailled descriptions in some cases.
Payment methods are built as pretix plugins. For this reason, you might first need to enable a certain plugin at
"Settings" → "Plugins" in your event settings. Then, you can configure them in detail at "Settings" -> "Payment".
@@ -10,7 +10,25 @@ Payment methods are built as pretix plugins. For this reason, you might first ne
If you host pretix on your own server, you might need to install a plugin first for some of the payment methods listed
on this page as well as for additional ones.
To get an overview of the officially supported payment methods and their pros and cons, head to the `pretix website`_.
On these pages, you get more information on how to configure :ref:`stripe`, :ref:`paypal`, and :ref:`banktransfer`.
:ref:`stripe`
Stripe is a US-based company that offers you an easy way to accept credit card payments from all over the world.
To accept payments with Stripe, you need to have a Stripe merchant account that is easy to create. Click on the link
above to get more details about the Stripe integration into pretix.
:ref:`paypal`
If you want to accept online payments via PayPal, you can do so using pretix. You will need a PayPal merchant
account and it is a little bit complicated to obtain the required technical details, but we've got you covered.
Click on the link above to learn more.
:ref:`banktransfer`
Classical IBAN wire transfers are a common payment method in central Europe that has the large benefit that it
often does not cause any additional fees. However, it requires you to invest some more effort as you need to
check your bank account for incoming payments regularly. We provide some tools to make this easier for you.
SEPA debit
In some Europen countries, a very popular online payment method is SEPA direct debit. If you want to offer this
option in your pretix ticket shop, we provide a convenient plugin that allows users to enter their SEPA bank
account details and issue a SEPA mandate. You will then need to regularly download a SEPA XML file from pretix
and upload it to your bank's interface to actually perform the debits.
.. _pretix website: https://pretix.eu/about/en/payments

View File

@@ -1,65 +0,0 @@
General settings
================
At "Settings" → "Pages", you can configure every aspect related to the payments you want to accept. The upper part
of the page shows a number of general settings that affect all payment methods:
.. thumbnail:: ../../screens/event/settings_payment.png
:align: center
:class: screenshot
In particular, these are:
Payment term in days
If a order has been created, it is supposed to be paid within this number of days. Of course, some payment methods
(like credit card) succeed immediately in most cases, but others don't (like bank transfer) and even credit card
payments might fail and you might want to give the customer a chance to try another credit card before losing their
ticket. Therefore, we recommend setting a few days here. If you are accepting bank transfers, we wouldn't recommend
less than 10 days.
Last date of payments
There is probably no use for payments received after your event, so you can set a date that the payment deadline of
a new order will never exceed. This has precedence over the number of days configured above, so if I create an order
two days before the configured last date of payments, my payment term will only be two days, not ten. If you have
payment methods that always require some time (like bank transfer), you will later be able to selectively disable them
once the event comes closer.
Only end payment terms on weekdays
If you check this box, the payment term calculated by the number of days configured above will never end on a Saturday
or a Sunday. If it technically would do so, the term is extended to the next Monday. Note that this currently does not
take into account national or bank holidays in your country.
Automatically expire unpaid orders
If you check this box, orders will automatically go into "expired" state if the payment term is over and no payment
has been received. This means that the tickets will no longer be reserved for the customer and someone else can buy
them from the shop again. If you do not check this box, tickets do not become available again automatically, but you
can mark orders as expired manually.
Accept late payments
If you check this box, incoming payments will accepted even if the order is in "expired" state -- as long as there
still is sufficient quota available and the last date of payments is not yet over. We recommend to check this in most
cases.
Tax rule for payment fees
If you pass on the payment method fees to your customers, you will most likely also need to pay sales tax on those
fees. Here, you can configure the tax rate. Read :ref:`taxes` for more information.
Below, you can configure the details of the various payment methods. You can find information on their different settings
on the next pages of this documentation, but there are a few things most of them have in common:
Enable payment method
Check this box to allow customers to use this method. At least one method needs to be active to process non-free orders.
Additional fee (absolute and percentage), Calculate the fee from the total value including the fee
These fields allow you to pass fees on to your customers instead of paying them yourselves. Read :ref:`payment-fees`
for documentation on how this behaves.
Available until
This allows you to set a date at which this payment method will automatically become disabled. This is useful if you
want people to be able to pay by card on the day before your event, but not by bank transfer, because it would not
arrive in time.
Text on invoices
If you are using pretix' invoicing feature, this is a text that will be printed on every invoice for an order that
uses this payment method. You could use this to tell the accounting department of the invoice receiver that the payment
has already been received online or that it should be performed via bank transfer.

View File

@@ -1,17 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 149.59399 149.59399"
version="1.1"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="149.59399"
height="149.59399"
id="svg2"
height="159.56693"
width="159.56693">
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="icon_draft.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.959798"
inkscape:cx="141.14985"
inkscape:cy="82.686886"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1914"
inkscape:window-height="1039"
inkscape:window-x="1920"
inkscape:window-y="18"
inkscape:window-maximized="0"
fit-margin-top="20"
fit-margin-left="20"
fit-margin-right="20"
fit-margin-bottom="20" />
<metadata
id="metadata7">
<rdf:RDF>
@@ -25,12 +52,15 @@
</rdf:RDF>
</metadata>
<g
transform="translate(-257.78125,-548.74975)"
id="layer1">
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-257.78125,-548.74975)">
<path
style="color:#000000;fill:#3b1c4a;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 277.78125,568.74975 0,34.09383 c 11.37842,0 20.613,9.28198 20.613,20.7188 0,11.4368 -9.23458,20.68754 -20.613,20.68754 l 0,34.09383 68.33691,0 0,-9.50002 2.98469,0 0,9.50002 38.2724,0 0,-34.09383 c -0.0104,2e-5 -0.0207,0 -0.031,0 -11.37841,0 -20.613,-9.25074 -20.613,-20.68754 0,-11.43682 9.23459,-20.7188 20.613,-20.7188 0.0105,0 0.0207,-2e-5 0.031,0 l 0,-34.09383 -38.2724,0 0,9.09377 -2.98469,0 0,-9.09377 z m 68.33691,16.09379 2.98469,0 0,14.00003 -2.98469,0 z m 0,21.00004 2.98469,0 0,14.00004 -2.98469,0 z m -24.40604,3.68751 c 4.02516,3e-5 7.02244,1.10354 8.98515,3.34376 1.96268,2.20685 2.92251,5.27326 2.92251,9.21877 l 0,3.87501 c 0,3.94554 -0.95983,7.04102 -2.92251,9.28127 -1.96271,2.20683 -4.95999,3.31251 -8.98515,3.31251 -0.5988,0 -1.31361,-0.0268 -2.14524,-0.0937 -0.83167,-0.0334 -1.71126,-0.11626 -2.64269,-0.25 l 0,8.87502 c -1e-5,0.26748 -0.11132,0.48687 -0.31091,0.6875 -0.19961,0.20061 -0.41788,0.31249 -0.68399,0.3125 l -4.60139,0 c -0.26614,-1e-5 -0.4844,-0.11189 -0.684,-0.3125 -0.19959,-0.20063 -0.3109,-0.42002 -0.3109,-0.6875 l 0,-34.90633 c 0,-0.36777 0.0824,-0.64529 0.24872,-0.8125 0.16633,-0.20059 0.52265,-0.39529 1.08817,-0.5625 1.5635,-0.40121 3.24248,-0.70343 5.00557,-0.93751 1.76309,-0.23403 3.43988,-0.34372 5.03666,-0.34375 z m 0,5.40627 c -0.89819,2e-5 -1.80453,0.0269 -2.73596,0.0937 -0.89819,0.0669 -1.58626,0.14971 -2.05197,0.25 l 0,17.50004 c 0.69857,0.10031 1.49359,0.18313 2.42505,0.25 0.9647,0.0669 1.76408,0.12501 2.36288,0.125 1.06449,10e-6 1.94409,-0.19469 2.64269,-0.5625 0.69857,-0.36781 1.24859,-0.86471 1.6478,-1.50001 0.39917,-0.63529 0.67527,-1.38064 0.80835,-2.25 0.16631,-0.86934 0.24871,-1.83846 0.24873,-2.87501 l 0,-3.87501 c -2e-5,-1.03651 -0.0824,-1.97438 -0.24873,-2.84375 -0.13308,-0.86934 -0.40918,-1.61469 -0.80835,-2.25001 -0.39921,-0.63527 -0.94923,-1.13218 -1.6478,-1.5 -0.6986,-0.36778 -1.5782,-0.56248 -2.64269,-0.5625 z m 24.40604,11.90627 2.98469,0 0,14.00003 -2.98469,0 z m 0,21.00005 2.98469,0 0,14.00003 -2.98469,0 z"
id="rect3888"
transform="matrix(0.93749999,0,0,0.93749999,257.78125,548.74975)"
d="M 21.333984 21.333984 L 21.333984 57.699219 C 33.470966 57.699219 43.320312 67.601506 43.320312 79.800781 C 43.320312 92.000035 33.470966 101.86719 21.333984 101.86719 L 21.333984 138.23438 L 94.226562 138.23438 L 94.226562 128.09961 L 97.410156 128.09961 L 97.410156 138.23438 L 138.23438 138.23438 L 138.23438 101.86719 C 138.22328 101.86721 138.21216 101.86719 138.20117 101.86719 C 126.0642 101.86719 116.21289 92.000035 116.21289 79.800781 C 116.21289 67.601506 126.0642 57.699219 138.20117 57.699219 C 138.21237 57.699219 138.22339 57.699197 138.23438 57.699219 L 138.23438 21.333984 L 97.410156 21.333984 L 97.410156 31.033203 L 94.226562 31.033203 L 94.226562 21.333984 L 21.333984 21.333984 z M 94.226562 38.5 L 97.410156 38.5 L 97.410156 53.433594 L 94.226562 53.433594 L 94.226562 38.5 z M 94.226562 60.900391 L 97.410156 60.900391 L 97.410156 75.833984 L 94.226562 75.833984 L 94.226562 60.900391 z M 67.044922 64.027344 C 76.359333 64.027344 82.662109 68.991742 82.662109 79.533203 C 82.662109 89.014942 77.139434 95.039062 69.386719 95.039062 C 67.490377 95.039062 65.927327 94.814901 65.146484 94.591797 L 65.146484 106.64062 L 54.550781 106.64062 L 54.550781 66.314453 C 57.395304 64.97585 61.244324 64.027344 67.044922 64.027344 z M 66.990234 70.216797 C 66.209392 70.216797 65.648458 70.328766 65.146484 70.496094 L 65.146484 88.568359 C 65.536906 88.735677 66.097199 88.845703 66.822266 88.845703 C 70.61497 88.845703 72.175781 85.725087 72.175781 79.589844 C 72.175781 73.287273 70.838704 70.216797 66.990234 70.216797 z M 94.226562 83.300781 L 97.410156 83.300781 L 97.410156 98.234375 L 94.226562 98.234375 L 94.226562 83.300781 z M 94.226562 105.69922 L 97.410156 105.69922 L 97.410156 120.63281 L 94.226562 120.63281 L 94.226562 105.69922 z "
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#3b1c4a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.06666672;marker:none;enable-background:accumulate" />
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccsccccccccssscccccccccccccccccccsscscccccccssccccccccccccccccccccccccccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -9,16 +9,15 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="600"
height="400"
width="294.15625"
height="149.59375"
id="svg2"
version="1.1"
inkscape:version="0.92.1 r"
inkscape:version="0.91 r13725"
sodipodi:docname="logo.svg"
inkscape:export-filename="/tmp/LOGO.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
viewBox="0 0 562.50001 375.00002">
inkscape:export-filename="/home/raphael/proj/pretix/pretix/logo_draft.png"
inkscape:export-xdpi="88.529999"
inkscape:export-ydpi="88.529999">
<defs
id="defs4" />
<sodipodi:namedview
@@ -29,14 +28,14 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.9899495"
inkscape:cx="133.36756"
inkscape:cy="276.10571"
inkscape:cx="134.70089"
inkscape:cy="277.43904"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1916"
inkscape:window-width="636"
inkscape:window-height="1041"
inkscape:window-x="1920"
inkscape:window-x="3200"
inkscape:window-y="18"
inkscape:window-maximized="0"
fit-margin-top="20"
@@ -59,14 +58,11 @@
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-259.03125,-322.09374)">
transform="translate(-257.78125,-548.75)">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#3b1c4a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.91138947;marker:none;enable-background:accumulate"
d="m 297.38548,404.85558 v 65.16643 c 21.86016,0 39.6016,17.74144 39.6016,39.60159 0,21.86015 -17.74144,39.54187 -39.6016,39.54187 v 65.16644 h 280.37693 v -18.1582 h 5.73417 v 18.1582 h 199.68046 v -65.16644 c -0.02,4e-5 -0.0397,0 -0.0596,0 -21.86015,0 -39.6016,-17.68172 -39.6016,-39.54187 0,-21.86015 17.74145,-39.60159 39.6016,-39.60159 0.02,0 0.0397,-4e-5 0.0596,0 V 404.85558 H 583.49658 v 17.38169 h -5.73417 V 404.85558 Z M 577.76241,435.617 h 5.73417 v 26.75945 h -5.73417 z m 79.21068,24.53074 c 5.33405,0 9.60172,3.68466 9.60172,8.24287 0,4.5582 -4.26767,8.33993 -9.60172,8.33993 -5.2371,0 -9.50469,-3.78173 -9.50469,-8.33993 0,-4.55821 4.26759,-8.24287 9.50469,-8.24287 z m -25.28486,8.04874 v 15.32472 h 7.56717 v 12.02458 h -7.56717 v 23.47051 c 0,3.87934 1.35737,5.33473 4.0729,5.33473 1.35775,0 2.03951,-0.19461 3.49427,-0.77651 v 11.34514 c -1.35777,0.77585 -4.56174,1.84419 -8.829,1.84419 -11.73495,0 -17.16515,-7.17511 -17.16515,-16.19455 v -25.02351 h -5.43179 V 483.5212 h 5.43179 v -10.66944 z m -53.92582,7.5597 h 5.73417 v 26.75945 h -5.73417 z m -142.52917,6.79439 c 16.19618,0 27.15517,8.63124 27.15517,26.96104 0,16.48713 -9.6016,26.96105 -23.08227,26.96105 -3.29741,0 -6.01528,-0.38857 -7.37304,-0.77651 v 20.95062 H 413.50612 V 486.5264 c 4.94614,-2.32759 11.64087,-3.97583 21.72712,-3.97583 z m 95.30815,0 c 16.39014,0 24.14917,11.34479 23.17933,29.09269 l -30.45158,4.27076 c 1.1638,5.62501 4.16799,8.437 10.85985,8.437 6.20689,0 11.05633,-1.36007 13.96583,-2.81482 l 4.0729,11.2518 c -4.5582,2.23062 -10.6662,3.97584 -20.36451,3.97584 -17.06903,0 -26.08749,-11.54096 -26.08749,-27.25223 0,-15.71126 8.43552,-26.96104 24.82567,-26.96104 z m -34.25568,0.0113 c 1.70649,0.026 3.49392,0.0859 5.36084,0.18292 l -3.20307,12.80108 c -3.39442,-1.6487 -6.69185,-1.74642 -9.11643,-0.87356 v 41.121 h -18.42698 v -49.2668 c 5.3462,-2.63068 13.44024,-4.1463 25.38564,-3.96465 z m 151.47387,0.95943 h 18.42699 v 52.27202 h -18.42699 z m 25.29605,0 h 19.20348 l 6.30535,13.09227 h 0.19412 l 6.884,-13.09227 h 17.16517 l -15.22393,24.14622 16.67986,28.1258 h -20.3645 l -6.69361,-14.45115 h -0.19412 l -6.98104,14.45115 h -18.62112 l 16.38867,-26.96104 z m -143.29075,9.60175 c -5.43106,0 -8.43651,4.94275 -7.85461,14.05916 l 14.8394,-2.22871 c 0,-7.9526 -2.3296,-11.83045 -6.98479,-11.83045 z m -94.6287,0.19038 c -1.35776,0 -2.33024,0.19437 -3.20308,0.48532 v 31.4222 c 0.67888,0.29093 1.65112,0.48531 2.91189,0.48531 6.59487,0 9.31055,-5.42933 9.31055,-16.09748 0,-10.95908 -2.32753,-16.29535 -9.01936,-16.29535 z m 142.62623,22.58194 h 5.73417 v 26.75946 h -5.73417 z m 0,40.13918 h 5.73417 v 26.75946 h -5.73417 z"
id="rect3888"
inkscape:connector-curvature="0"
inkscape:export-filename="/tmp/LOGO.png"
inkscape:export-xdpi="88"
inkscape:export-ydpi="88" />
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#3b1c4a;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;enable-background:accumulate"
d="M 20 20 L 20 54.09375 C 31.43679 54.09375 40.71875 63.37571 40.71875 74.8125 C 40.71875 86.24928 31.43679 95.5 20 95.5 L 20 129.59375 L 166.6875 129.59375 L 166.6875 120.09375 L 169.6875 120.09375 L 169.6875 129.59375 L 274.15625 129.59375 L 274.15625 95.5 C 274.14575 95.50002 274.1354 95.5 274.125 95.5 C 262.68822 95.5 253.40625 86.24928 253.40625 74.8125 C 253.40625 63.37571 262.68822 54.09375 274.125 54.09375 C 274.1355 54.09375 274.14585 54.09373 274.15625 54.09375 L 274.15625 20 L 169.6875 20 L 169.6875 29.09375 L 166.6875 29.09375 L 166.6875 20 L 20 20 z M 166.6875 36.09375 L 169.6875 36.09375 L 169.6875 50.09375 L 166.6875 50.09375 L 166.6875 36.09375 z M 208.12891 48.927734 C 210.91958 48.927734 213.15234 50.855474 213.15234 53.240234 C 213.15234 55.624994 210.91958 57.603516 208.12891 57.603516 C 205.38897 57.603516 203.15625 55.624994 203.15625 53.240234 C 203.15625 50.855474 205.38897 48.927734 208.12891 48.927734 z M 194.90039 53.138672 L 194.90039 61.15625 L 198.85938 61.15625 L 198.85938 67.447266 L 194.90039 67.447266 L 194.90039 79.726562 C 194.90039 81.756152 195.61054 82.517578 197.03125 82.517578 C 197.7416 82.517578 198.09828 82.415768 198.85938 82.111328 L 198.85938 88.046875 C 198.14902 88.452785 196.47277 89.011719 194.24023 89.011719 C 188.10074 89.011719 185.25977 85.257843 185.25977 80.539062 L 185.25977 67.447266 L 182.41797 67.447266 L 182.41797 61.15625 L 185.25977 61.15625 L 185.25977 55.574219 L 194.90039 53.138672 z M 166.6875 57.09375 L 169.6875 57.09375 L 169.6875 71.09375 L 166.6875 71.09375 L 166.6875 57.09375 z M 92.119141 60.648438 C 100.59265 60.648438 106.32617 65.164126 106.32617 74.753906 C 106.32617 83.379636 101.30281 88.859375 94.25 88.859375 C 92.52486 88.859375 91.102928 88.656085 90.392578 88.453125 L 90.392578 99.414062 L 80.751953 99.414062 L 80.751953 62.728516 C 83.339673 61.510766 86.842221 60.648435 92.119141 60.648438 z M 141.98242 60.648438 C 150.55741 60.648438 154.61678 66.583801 154.10938 75.869141 L 138.17773 78.103516 C 138.78661 81.046406 140.35834 82.517578 143.85938 82.517578 C 147.1067 82.517578 149.64383 81.806022 151.16602 81.044922 L 153.29688 86.931641 C 150.91212 88.098651 147.71654 89.011719 142.64258 89.011719 C 133.71241 89.011719 128.99414 82.973726 128.99414 74.753906 C 128.99414 66.534096 133.40743 60.648438 141.98242 60.648438 z M 124.06055 60.654297 C 124.95335 60.667874 125.8885 60.69926 126.86523 60.75 L 125.18945 67.447266 C 123.41356 66.584696 121.68841 66.533574 120.41992 66.990234 L 120.41992 88.503906 L 110.7793 88.503906 L 110.7793 62.728516 C 113.57632 61.352202 117.81096 60.559256 124.06055 60.654297 z M 203.30859 61.15625 L 212.94922 61.15625 L 212.94922 88.503906 L 203.30859 88.503906 L 203.30859 61.15625 z M 216.54297 61.15625 L 226.58984 61.15625 L 229.88867 68.005859 L 229.99023 68.005859 L 233.5918 61.15625 L 242.57227 61.15625 L 234.60742 73.789062 L 243.33398 88.503906 L 232.67969 88.503906 L 229.17773 80.943359 L 229.07617 80.943359 L 225.42383 88.503906 L 215.68164 88.503906 L 224.25586 74.398438 L 216.54297 61.15625 z M 141.57617 66.179688 C 138.73475 66.179688 137.16236 68.765636 137.4668 73.535156 L 145.23047 72.369141 C 145.23047 68.208501 144.01167 66.179687 141.57617 66.179688 z M 92.068359 66.279297 C 91.358009 66.279297 90.849228 66.380983 90.392578 66.533203 L 90.392578 82.972656 C 90.747748 83.124866 91.256406 83.226562 91.916016 83.226562 C 95.366316 83.226562 96.787109 80.386048 96.787109 74.804688 C 96.787109 69.071117 95.569389 66.279297 92.068359 66.279297 z M 166.6875 78.09375 L 169.6875 78.09375 L 169.6875 92.09375 L 166.6875 92.09375 L 166.6875 78.09375 z M 166.6875 99.09375 L 169.6875 99.09375 L 169.6875 113.09375 L 166.6875 113.09375 L 166.6875 99.09375 z "
transform="translate(257.78125,548.75)"
id="rect3888" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -6,7 +6,7 @@ localecompile:
localegen:
./manage.py makemessages --all --ignore "pretix/helpers/*"
./manage.py makemessages --all -d djangojs --ignore "pretix/helpers/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static.dist/*" --ignore "data/*" --ignore "build/*"
./manage.py makemessages --all -d djangojs --ignore "pretix/helpers/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static.dist/*" --ignore "build/*"
staticfiles: jsi18n
./manage.py collectstatic --noinput

View File

@@ -1 +1 @@
__version__ = "1.11.1"
__version__ = "1.8.1"

View File

@@ -2,7 +2,6 @@ import time
from django.conf import settings
from django.contrib.auth import logout
from rest_framework.exceptions import PermissionDenied
from rest_framework.permissions import SAFE_METHODS, BasePermission
from pretix.base.models import Event
@@ -14,15 +13,10 @@ class EventPermission(BasePermission):
def has_permission(self, request, view):
if not request.user.is_authenticated and not isinstance(request.auth, TeamAPIToken):
if request.method in SAFE_METHODS and request.path.startswith('/api/v1/docs/'):
return True
return False
if request.method not in SAFE_METHODS and hasattr(view, 'write_permission'):
required_permission = getattr(view, 'write_permission')
elif hasattr(view, 'permission'):
required_permission = getattr(view, 'permission')
else:
required_permission = None
if request.user.is_authenticated:
# If this logic is updated, make sure to also update the logic in pretix/control/middleware.py
if not settings.PRETIX_LONG_SESSIONS or not request.session.get('pretix_auth_long_session', False):
@@ -47,8 +41,9 @@ class EventPermission(BasePermission):
request.organizer = request.event.organizer
request.eventpermset = perm_holder.get_event_permission_set(request.organizer, request.event)
if required_permission and required_permission not in request.eventpermset:
return False
if hasattr(view, 'permission'):
if view.permission and view.permission not in request.eventpermset:
return False
elif 'organizer' in request.resolver_match.kwargs:
request.organizer = Organizer.objects.filter(
@@ -58,21 +53,7 @@ class EventPermission(BasePermission):
return False
request.orgapermset = perm_holder.get_organizer_permission_set(request.organizer)
if required_permission and required_permission not in request.orgapermset:
return False
if hasattr(view, 'permission'):
if view.permission and view.permission not in request.orgapermset:
return False
return True
def permission_required(required_permission):
def decorator(function):
def wrapper(self, request, *args, **kw):
if 'event' in request.resolver_match.kwargs and 'organizer' in request.resolver_match.kwargs:
if required_permission and required_permission not in request.eventpermset:
raise PermissionDenied('You do not have permission to perform this operation.')
elif 'organizer' in request.resolver_match.kwargs:
if required_permission and required_permission not in request.orgapermset:
raise PermissionDenied('You do not have permission to perform this operation.')
return function(self, request, *args, **kw)
return wrapper
return decorator

View File

@@ -1,16 +0,0 @@
from rest_framework.response import Response
from rest_framework.views import exception_handler, status
from pretix.base.services.locking import LockTimeoutException
def custom_exception_handler(exc, context):
response = exception_handler(exc, context)
if isinstance(exc, LockTimeoutException):
response = Response(
{'detail': 'The server was too busy to process your request. Please try again.'},
status=status.HTTP_409_CONFLICT
)
return response

View File

@@ -1,37 +0,0 @@
from django.utils.translation import ugettext as _
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.base.models import CheckinList
class CheckinListSerializer(I18nAwareModelSerializer):
checkin_count = serializers.IntegerField(read_only=True)
position_count = serializers.IntegerField(read_only=True)
class Meta:
model = CheckinList
fields = ('id', 'name', 'all_products', 'limit_products', 'subevent', 'checkin_count', 'position_count')
def validate(self, data):
data = super().validate(data)
event = self.context['event']
full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
full_data.update(data)
for item in full_data.get('limit_products'):
if event != item.event:
raise ValidationError(_('One or more items do not belong to this event.'))
if event.has_subevents:
if not full_data.get('subevent'):
raise ValidationError(_('Subevent cannot be null for event series.'))
if event != full_data.get('subevent').event:
raise ValidationError(_('The subevent does not belong to this event.'))
else:
if full_data.get('subevent'):
raise ValidationError(_('The subevent does not belong to this event.'))
return data

View File

@@ -1,7 +1,5 @@
from django.conf import settings
from i18nfield.fields import I18nCharField, I18nTextField
from i18nfield.strings import LazyI18nString
from rest_framework.exceptions import ValidationError
from rest_framework.fields import Field
from rest_framework.serializers import ModelSerializer
@@ -24,16 +22,6 @@ class I18nField(Field):
settings.LANGUAGE_CODE: str(value.data)
}
def to_internal_value(self, data):
if isinstance(data, str):
return LazyI18nString(data)
elif isinstance(data, dict):
if any([k not in dict(settings.LANGUAGES) for k in data.keys()]):
raise ValidationError('Invalid languages included.')
return LazyI18nString(data)
else:
raise ValidationError('Invalid data type.')
class I18nAwareModelSerializer(ModelSerializer):
pass

View File

@@ -73,16 +73,3 @@ class QuotaSerializer(I18nAwareModelSerializer):
class Meta:
model = Quota
fields = ('id', 'name', 'size', 'items', 'variations', 'subevent')
def validate(self, data):
data = super().validate(data)
event = self.context['event']
full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
full_data.update(data)
Quota.clean_variations(full_data.get('items'), full_data.get('variations'))
Quota.clean_items(event, full_data.get('items'), full_data.get('variations'))
Quota.clean_subevent(event, full_data.get('subevent'))
return data

View File

@@ -26,7 +26,7 @@ class InvoiceAdddressSerializer(I18nAwareModelSerializer):
class Meta:
model = InvoiceAddress
fields = ('last_modified', 'is_business', 'company', 'name', 'street', 'zipcode', 'city', 'country', 'vat_id',
'vat_id_validated', 'internal_reference')
'vat_id_validated')
class AnswerSerializer(I18nAwareModelSerializer):
@@ -38,7 +38,7 @@ class AnswerSerializer(I18nAwareModelSerializer):
class CheckinSerializer(I18nAwareModelSerializer):
class Meta:
model = Checkin
fields = ('datetime', 'list')
fields = ('datetime',)
class OrderDownloadsField(serializers.Field):
@@ -153,5 +153,4 @@ class InvoiceSerializer(I18nAwareModelSerializer):
model = Invoice
fields = ('order', 'number', 'is_cancellation', 'invoice_from', 'invoice_to', 'date', 'refers', 'locale',
'introductory_text', 'additional_text', 'payment_provider_text', 'footer_text', 'lines',
'foreign_currency_display', 'foreign_currency_rate', 'foreign_currency_rate_date',
'internal_reference')
'foreign_currency_display', 'foreign_currency_rate', 'foreign_currency_rate_date')

View File

@@ -8,36 +8,3 @@ class VoucherSerializer(I18nAwareModelSerializer):
fields = ('id', 'code', 'max_usages', 'redeemed', 'valid_until', 'block_quota',
'allow_ignore_quota', 'price_mode', 'value', 'item', 'variation', 'quota',
'tag', 'comment', 'subevent')
read_only_fields = ('id', 'redeemed')
def validate(self, data):
data = super().validate(data)
full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
full_data.update(data)
Voucher.clean_item_properties(
full_data, self.context.get('event'),
full_data.get('quota'), full_data.get('item'), full_data.get('variation')
)
Voucher.clean_subevent(
full_data, self.context.get('event')
)
Voucher.clean_max_usages(full_data, self.instance.redeemed if self.instance else 0)
check_quota = Voucher.clean_quota_needs_checking(
full_data, self.instance,
item_changed=self.instance and (
full_data.get('item') != self.instance.item or
full_data.get('variation') != self.instance.variation or
full_data.get('quota') != self.instance.quota
),
creating=not self.instance
)
if check_quota:
Voucher.clean_quota_check(
full_data, 1, self.instance, self.context.get('event'),
full_data.get('quota'), full_data.get('item'), full_data.get('variation')
)
Voucher.clean_voucher_code(full_data, self.context.get('event'), self.instance.pk if self.instance else None)
return data

View File

View File

@@ -0,0 +1,19 @@
{% extends "rest_framework/base.html" %}
{% load staticfiles %}
{% load compress %}
{% block bootstrap_theme %}
{% compress css %}
<link rel="stylesheet" type="text/x-scss" href="{% static "rest_framework/scss/main.scss" %}" />
{% endcompress %}
{% endblock %}
{% block branding %}
<a class="navbar-brand" href="/api/v1/">pretix REST API</a>
{% endblock %}
{% block description %}
<div class="alert alert-info alert-docs-link">
<a href="https://docs.pretix.eu/en/latest/api/index.html">
You can find documentation on our REST API on docs.pretix.eu.
</a>
</div>
{% endblock %}

View File

@@ -4,7 +4,7 @@ from django.apps import apps
from django.conf.urls import include, url
from rest_framework import routers
from .views import checkin, event, item, order, organizer, voucher, waitinglist
from .views import event, item, order, organizer, voucher, waitinglist
router = routers.DefaultRouter()
router.register(r'organizers', organizer.OrganizerViewSet)
@@ -24,10 +24,6 @@ event_router.register(r'orderpositions', order.OrderPositionViewSet)
event_router.register(r'invoices', order.InvoiceViewSet)
event_router.register(r'taxrules', event.TaxRuleViewSet)
event_router.register(r'waitinglistentries', waitinglist.WaitingListViewSet)
event_router.register(r'checkinlists', checkin.CheckinListViewSet)
checkinlist_router = routers.DefaultRouter()
checkinlist_router.register(r'positions', checkin.CheckinListPositionViewSet)
# Force import of all plugins to give them a chance to register URLs with the router
for app in apps.get_app_configs():
@@ -39,6 +35,4 @@ urlpatterns = [
url(r'^', include(router.urls)),
url(r'^organizers/(?P<organizer>[^/]+)/', include(orga_router.urls)),
url(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/', include(event_router.urls)),
url(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/checkinlists/(?P<list>[^/]+)/',
include(checkinlist_router.urls)),
]

View File

@@ -1,23 +0,0 @@
from rest_framework.filters import OrderingFilter
class RichOrderingFilter(OrderingFilter):
def filter_queryset(self, request, queryset, view):
ordering = self.get_ordering(request, queryset, view)
if ordering:
if hasattr(view, 'ordering_custom'):
newo = []
for ordering_part in ordering:
ob = view.ordering_custom.get(ordering_part)
if ob:
ob = dict(ob)
newo.append(ob.pop('_order'))
queryset = queryset.annotate(**ob)
else:
newo.append(ordering_part)
ordering = newo
return queryset.order_by(*ordering)
return queryset

View File

@@ -1,143 +0,0 @@
import django_filters
from django.db.models import F, Max, OuterRef, Prefetch, Q, Subquery
from django.db.models.functions import Coalesce
from django.shortcuts import get_object_or_404
from django.utils.functional import cached_property
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from rest_framework import viewsets
from pretix.api.serializers.checkin import CheckinListSerializer
from pretix.api.serializers.order import OrderPositionSerializer
from pretix.api.views import RichOrderingFilter
from pretix.base.models import Checkin, CheckinList, Order, OrderPosition
from pretix.base.models.organizer import TeamAPIToken
from pretix.helpers.database import FixedOrderBy
class CheckinListFilter(FilterSet):
class Meta:
model = CheckinList
fields = ['subevent']
class CheckinListViewSet(viewsets.ModelViewSet):
serializer_class = CheckinListSerializer
queryset = CheckinList.objects.none()
filter_backends = (DjangoFilterBackend,)
filter_class = CheckinListFilter
permission = 'can_view_orders'
write_permission = 'can_change_event_settings'
def get_queryset(self):
qs = self.request.event.checkin_lists.prefetch_related(
'limit_products',
)
qs = CheckinList.annotate_with_numbers(qs, self.request.event)
return qs
def perform_create(self, serializer):
serializer.save(event=self.request.event)
serializer.instance.log_action(
'pretix.event.checkinlist.added',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
data=self.request.data
)
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['event'] = self.request.event
return ctx
def perform_update(self, serializer):
serializer.save(event=self.request.event)
serializer.instance.log_action(
'pretix.event.checkinlist.changed',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
data=self.request.data
)
def perform_destroy(self, instance):
instance.log_action(
'pretix.event.checkinlist.deleted',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
)
super().perform_destroy(instance)
class OrderPositionFilter(FilterSet):
order = django_filters.CharFilter(name='order', lookup_expr='code')
has_checkin = django_filters.rest_framework.BooleanFilter(method='has_checkin_qs')
attendee_name = django_filters.CharFilter(method='attendee_name_qs')
def has_checkin_qs(self, queryset, name, value):
return queryset.filter(last_checked_in__isnull=not value)
def attendee_name_qs(self, queryset, name, value):
return queryset.filter(Q(attendee_name=value) | Q(addon_to__attendee_name=value))
class Meta:
model = OrderPosition
fields = ['item', 'variation', 'attendee_name', 'secret', 'order', 'has_checkin', 'addon_to', 'subevent']
class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = OrderPositionSerializer
queryset = OrderPosition.objects.none()
filter_backends = (DjangoFilterBackend, RichOrderingFilter)
ordering = ('attendee_name', 'positionid')
ordering_fields = (
'order__code', 'order__datetime', 'positionid', 'attendee_name',
'last_checked_in', 'order__email',
)
ordering_custom = {
'attendee_name': {
'_order': F('display_name').asc(nulls_first=True),
'display_name': Coalesce('attendee_name', 'addon_to__attendee_name')
},
'-attendee_name': {
'_order': F('display_name').desc(nulls_last=True),
'display_name': Coalesce('attendee_name', 'addon_to__attendee_name')
},
'last_checked_in': {
'_order': FixedOrderBy(F('last_checked_in'), nulls_first=True),
},
'-last_checked_in': {
'_order': FixedOrderBy(F('last_checked_in'), nulls_last=True, descending=True),
},
}
filter_class = OrderPositionFilter
permission = 'can_view_orders'
@cached_property
def checkinlist(self):
return get_object_or_404(CheckinList, event=self.request.event, pk=self.kwargs.get("list"))
def get_queryset(self):
cqs = Checkin.objects.filter(
position_id=OuterRef('pk'),
list_id=self.checkinlist.pk
).order_by().values('position_id').annotate(
m=Max('datetime')
).values('m')
qs = OrderPosition.objects.filter(
order__event=self.request.event,
order__status=Order.STATUS_PAID,
subevent=self.checkinlist.subevent
).annotate(
last_checked_in=Subquery(cqs)
).prefetch_related(
Prefetch(
lookup='checkins',
queryset=Checkin.objects.filter(list_id=self.checkinlist.pk)
)
).select_related('item', 'variation', 'order', 'addon_to')
if not self.checkinlist.all_products:
qs = qs.filter(item__in=self.checkinlist.limit_products.values_list('id', flat=True))
return qs

View File

@@ -1,13 +1,11 @@
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from rest_framework import filters, viewsets
from rest_framework.exceptions import PermissionDenied
from pretix.api.serializers.event import (
EventSerializer, SubEventSerializer, TaxRuleSerializer,
)
from pretix.base.models import Event, ItemCategory, TaxRule
from pretix.base.models.event import SubEvent
from pretix.base.models.organizer import TeamAPIToken
class EventViewSet(viewsets.ReadOnlyModelViewSet):
@@ -38,39 +36,9 @@ class SubEventViewSet(viewsets.ReadOnlyModelViewSet):
)
class TaxRuleViewSet(viewsets.ModelViewSet):
class TaxRuleViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = TaxRuleSerializer
queryset = TaxRule.objects.none()
write_permission = 'can_change_event_settings'
def get_queryset(self):
return self.request.event.tax_rules.all()
def perform_update(self, serializer):
super().perform_update(serializer)
serializer.instance.log_action(
'pretix.event.taxrule.changed',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
data=self.request.data
)
def perform_create(self, serializer):
serializer.save(event=self.request.event)
serializer.instance.log_action(
'pretix.event.taxrule.added',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
data=self.request.data
)
def perform_destroy(self, instance):
if not instance.allow_delete():
raise PermissionDenied('This tax rule can not be deleted as it is currently in use.')
instance.log_action(
'pretix.event.taxrule.deleted',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
)
super().perform_destroy(instance)

View File

@@ -11,7 +11,6 @@ from pretix.api.serializers.item import (
QuotaSerializer,
)
from pretix.base.models import Item, ItemCategory, Question, Quota
from pretix.base.models.organizer import TeamAPIToken
class ItemFilter(FilterSet):
@@ -35,7 +34,6 @@ class ItemViewSet(viewsets.ReadOnlyModelViewSet):
ordering_fields = ('id', 'position')
ordering = ('position', 'id')
filter_class = ItemFilter
permission = 'can_change_items'
def get_queryset(self):
return self.request.event.items.select_related('tax_rule').prefetch_related('variations', 'addons').all()
@@ -54,7 +52,6 @@ class ItemCategoryViewSet(viewsets.ReadOnlyModelViewSet):
filter_class = ItemCategoryFilter
ordering_fields = ('id', 'position')
ordering = ('position', 'id')
permission = 'can_change_items'
def get_queryset(self):
return self.request.event.categories.all()
@@ -66,7 +63,6 @@ class QuestionViewSet(viewsets.ReadOnlyModelViewSet):
filter_backends = (OrderingFilter,)
ordering_fields = ('id', 'position')
ordering = ('position', 'id')
permission = 'can_change_items'
def get_queryset(self):
return self.request.event.questions.prefetch_related('options').all()
@@ -78,88 +74,17 @@ class QuotaFilter(FilterSet):
fields = ['subevent']
class QuotaViewSet(viewsets.ModelViewSet):
class QuotaViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = QuotaSerializer
queryset = Quota.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter,)
filter_class = QuotaFilter
ordering_fields = ('id', 'size')
ordering = ('id',)
permission = 'can_change_items'
write_permission = 'can_change_items'
def get_queryset(self):
return self.request.event.quotas.all()
def perform_create(self, serializer):
serializer.save(event=self.request.event)
serializer.instance.log_action(
'pretix.event.quota.added',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
data=self.request.data
)
if serializer.instance.subevent:
serializer.instance.subevent.log_action(
'pretix.subevent.quota.added',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
data=self.request.data
)
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['event'] = self.request.event
return ctx
def perform_update(self, serializer):
current_subevent = serializer.instance.subevent
serializer.save(event=self.request.event)
request_subevent = serializer.instance.subevent
serializer.instance.log_action(
'pretix.event.quota.changed',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
data=self.request.data
)
if current_subevent == request_subevent:
if current_subevent is not None:
current_subevent.log_action(
'pretix.subevent.quota.changed',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
data=self.request.data
)
else:
if request_subevent is not None:
request_subevent.log_action(
'pretix.subevent.quota.added',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
data=self.request.data
)
if current_subevent is not None:
current_subevent.log_action(
'pretix.subevent.quota.deleted',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
)
serializer.instance.rebuild_cache()
def perform_destroy(self, instance):
instance.log_action(
'pretix.event.quota.deleted',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
)
if instance.subevent:
instance.subevent.log_action(
'pretix.subevent.quota.deleted',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
)
super().perform_destroy(instance)
@detail_route(methods=['get'])
def availability(self, request, *args, **kwargs):
quota = self.get_object()

View File

@@ -1,28 +1,18 @@
import datetime
import django_filters
import pytz
from django.db.models import Q
from django.db.models.functions import Concat
from django.http import FileResponse
from django.utils.timezone import make_aware
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from rest_framework import serializers, status, viewsets
from rest_framework import viewsets
from rest_framework.decorators import detail_route
from rest_framework.exceptions import APIException, NotFound, PermissionDenied
from rest_framework.filters import OrderingFilter
from rest_framework.response import Response
from pretix.api.serializers.order import (
InvoiceSerializer, OrderPositionSerializer, OrderSerializer,
)
from pretix.base.models import Invoice, Order, OrderPosition, Quota
from pretix.base.models.organizer import TeamAPIToken
from pretix.base.models import Invoice, Order, OrderPosition
from pretix.base.services.invoices import invoice_pdf
from pretix.base.services.mail import SendMailException
from pretix.base.services.orders import (
OrderError, cancel_order, extend_order, mark_order_paid,
)
from pretix.base.services.tickets import (
get_cachedticket_for_order, get_cachedticket_for_position,
)
@@ -44,7 +34,6 @@ class OrderViewSet(viewsets.ReadOnlyModelViewSet):
filter_class = OrderFilter
lookup_field = 'code'
permission = 'can_view_orders'
write_permission = 'can_change_orders'
def get_queryset(self):
return self.request.event.orders.prefetch_related(
@@ -82,130 +71,6 @@ class OrderViewSet(viewsets.ReadOnlyModelViewSet):
)
return resp
@detail_route(methods=['POST'])
def mark_paid(self, request, **kwargs):
order = self.get_object()
if order.status in (Order.STATUS_PENDING, Order.STATUS_EXPIRED):
try:
mark_order_paid(
order, manual=True,
user=request.user if request.user.is_authenticated else None,
api_token=(request.auth if isinstance(request.auth, TeamAPIToken) else None),
)
except Quota.QuotaExceededException as e:
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
except SendMailException:
pass
return self.retrieve(request, [], **kwargs)
return Response(
{'detail': 'The order is not pending or expired.'},
status=status.HTTP_400_BAD_REQUEST
)
@detail_route(methods=['POST'])
def mark_canceled(self, request, **kwargs):
send_mail = request.data.get('send_email', True)
order = self.get_object()
if order.status != Order.STATUS_PENDING:
return Response(
{'detail': 'The order is not pending.'},
status=status.HTTP_400_BAD_REQUEST
)
cancel_order(
order,
user=request.user if request.user.is_authenticated else None,
api_token=(request.auth if isinstance(request.auth, TeamAPIToken) else None),
send_mail=send_mail
)
return self.retrieve(request, [], **kwargs)
@detail_route(methods=['POST'])
def mark_pending(self, request, **kwargs):
order = self.get_object()
if order.status != Order.STATUS_PAID:
return Response(
{'detail': 'The order is not paid.'},
status=status.HTTP_400_BAD_REQUEST
)
order.status = Order.STATUS_PENDING
order.payment_manual = True
order.save()
order.log_action(
'pretix.event.order.unpaid',
user=request.user if request.user.is_authenticated else None,
api_token=(request.auth if isinstance(request.auth, TeamAPIToken) else None),
)
return self.retrieve(request, [], **kwargs)
@detail_route(methods=['POST'])
def mark_expired(self, request, **kwargs):
order = self.get_object()
if order.status != Order.STATUS_PENDING:
return Response(
{'detail': 'The order is not pending.'},
status=status.HTTP_400_BAD_REQUEST
)
order.status = Order.STATUS_EXPIRED
order.save()
order.log_action(
'pretix.event.order.expired',
user=request.user if request.user.is_authenticated else None,
api_token=(request.auth if isinstance(request.auth, TeamAPIToken) else None),
)
return self.retrieve(request, [], **kwargs)
# TODO: Find a way to implement mark_refunded
@detail_route(methods=['POST'])
def extend(self, request, **kwargs):
new_date = request.data.get('expires', None)
force = request.data.get('force', False)
if not new_date:
return Response(
{'detail': 'New date is missing.'},
status=status.HTTP_400_BAD_REQUEST
)
df = serializers.DateField()
try:
new_date = df.to_internal_value(new_date)
except:
return Response(
{'detail': 'New date is invalid.'},
status=status.HTTP_400_BAD_REQUEST
)
tz = pytz.timezone(self.request.event.settings.timezone)
new_date = make_aware(datetime.datetime.combine(
new_date,
datetime.time(hour=23, minute=59, second=59)
), tz)
order = self.get_object()
try:
extend_order(
order,
new_date=new_date,
force=force,
user=request.user if request.user.is_authenticated else None,
api_token=(request.auth if isinstance(request.auth, TeamAPIToken) else None),
)
return self.retrieve(request, [], **kwargs)
except OrderError as e:
return Response(
{'detail': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
class OrderPositionFilter(FilterSet):
order = django_filters.CharFilter(name='order', lookup_expr='code')

View File

@@ -4,12 +4,10 @@ from django_filters.rest_framework import (
BooleanFilter, DjangoFilterBackend, FilterSet,
)
from rest_framework import viewsets
from rest_framework.exceptions import PermissionDenied
from rest_framework.filters import OrderingFilter
from pretix.api.serializers.voucher import VoucherSerializer
from pretix.base.models import Voucher
from pretix.base.models.organizer import TeamAPIToken
class VoucherFilter(FilterSet):
@@ -29,7 +27,7 @@ class VoucherFilter(FilterSet):
(Q(valid_until__isnull=False) & Q(valid_until__lte=now())))
class VoucherViewSet(viewsets.ModelViewSet):
class VoucherViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = VoucherSerializer
queryset = Voucher.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
@@ -37,49 +35,6 @@ class VoucherViewSet(viewsets.ModelViewSet):
ordering_fields = ('id', 'code', 'max_usages', 'valid_until', 'value')
filter_class = VoucherFilter
permission = 'can_view_vouchers'
write_permission = 'can_change_vouchers'
def get_queryset(self):
return self.request.event.vouchers.all()
def create(self, request, *args, **kwargs):
with request.event.lock():
return super().create(request, *args, **kwargs)
def perform_create(self, serializer):
serializer.save(event=self.request.event)
serializer.instance.log_action(
'pretix.voucher.added',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
data=self.request.data
)
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['event'] = self.request.event
return ctx
def update(self, request, *args, **kwargs):
with request.event.lock():
return super().update(request, *args, **kwargs)
def perform_update(self, serializer):
serializer.save(event=self.request.event)
serializer.instance.log_action(
'pretix.voucher.changed',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
data=self.request.data
)
def perform_destroy(self, instance):
if not instance.allow_delete():
raise PermissionDenied('This voucher can not be deleted as it has already been used.')
instance.log_action(
'pretix.voucher.deleted',
user=self.request.user,
api_token=(self.request.auth if isinstance(self.request.auth, TeamAPIToken) else None),
)
super().perform_destroy(instance)

View File

@@ -11,8 +11,7 @@ class PretixBaseConfig(AppConfig):
from . import payment # NOQA
from . import exporters # NOQA
from . import invoice # NOQA
from . import notifications # NOQA
from .services import export, mail, tickets, cart, orders, invoices, cleanup, update_check, quotas, notifications # NOQA
from .services import export, mail, tickets, cart, orders, invoices, cleanup, update_check, quotas # NOQA
try:
from .celery_app import app as celery_app # NOQA

View File

@@ -1,6 +1,6 @@
import hashlib
import time
from typing import Callable, Dict, List
from typing import Dict, List
from django.core.cache import caches
from django.db.models import Model
@@ -11,19 +11,17 @@ class NamespacedCache:
def __init__(self, prefixkey: str, cache: str='default'):
self.cache = caches[cache]
self.prefixkey = prefixkey
self._last_prefix = None
def _prefix_key(self, original_key: str, known_prefix=None) -> str:
def _prefix_key(self, original_key: str) -> str:
# Race conditions can happen here, but should be very very rare.
# We could only handle this by going _really_ lowlevel using
# memcached's `add` keyword instead of `set`.
# See also:
# https://code.google.com/p/memcached/wiki/NewProgrammingTricks#Namespacing
prefix = known_prefix or self.cache.get(self.prefixkey)
prefix = self.cache.get(self.prefixkey)
if prefix is None:
prefix = int(time.time())
self.cache.set(self.prefixkey, prefix)
self._last_prefix = prefix
key = '%s:%d:%s' % (self.prefixkey, prefix, original_key)
if len(key) > 200: # Hash long keys, as memcached has a length limit
# TODO: Use a more efficient, non-cryptographic hash algorithm
@@ -34,25 +32,17 @@ class NamespacedCache:
return key.split(":", 2 + self.prefixkey.count(":"))[-1]
def clear(self) -> None:
self._last_prefix = None
try:
prefix = self.cache.incr(self.prefixkey, 1)
except ValueError:
prefix = int(time.time())
self.cache.set(self.prefixkey, prefix)
def set(self, key: str, value: str, timeout: int=300):
def set(self, key: str, value: str, timeout: int=3600):
return self.cache.set(self._prefix_key(key), value, timeout)
def get(self, key: str) -> str:
return self.cache.get(self._prefix_key(key, known_prefix=self._last_prefix))
def get_or_set(self, key: str, default: Callable, timeout=300) -> str:
return self.cache.get_or_set(
self._prefix_key(key, known_prefix=self._last_prefix),
default=default,
timeout=timeout
)
return self.cache.get(self._prefix_key(key))
def get_many(self, keys: List[str]) -> Dict[str, str]:
values = self.cache.get_many([self._prefix_key(key) for key in keys])
@@ -61,7 +51,7 @@ class NamespacedCache:
newvalues[self._strip_prefix(k)] = v
return newvalues
def set_many(self, values: Dict[str, str], timeout=300):
def set_many(self, values: Dict[str, str], timeout=3600):
newvalues = {}
for k, v in values.items():
newvalues[self._prefix_key(k)] = v

View File

@@ -25,7 +25,7 @@ class BaseExporter:
"""
A short and unique identifier for this exporter.
This should only contain lowercase letters and in most
cases will be the same as your package name.
cases will be the same as your packagename.
"""
raise NotImplementedError() # NOQA

View File

@@ -25,9 +25,7 @@ class AnswerFilesExporter(BaseExporter):
forms.ModelMultipleChoiceField(
queryset=self.event.questions.filter(type='F'),
label=_('Questions'),
widget=forms.CheckboxSelectMultiple(
attrs={'class': 'scrolling-multiple-choice'}
),
widget=forms.CheckboxSelectMultiple,
required=False
)),
]

View File

@@ -1,10 +1,7 @@
import os
import tempfile
from collections import OrderedDict
from zipfile import ZipFile
import dateutil.parser
from django import forms
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
@@ -18,26 +15,9 @@ class InvoiceExporter(BaseExporter):
verbose_name = _('All invoices')
def render(self, form_data: dict):
qs = self.event.invoices.all()
if form_data.get('payment_provider'):
qs = qs.filter(order__payment_provider=form_data.get('payment_provider'))
if form_data.get('date_from'):
date_value = form_data.get('date_from')
if isinstance(date_value, str):
date_value = dateutil.parser.parse(date_value).date()
qs = qs.filter(date__gte=date_value)
if form_data.get('date_to'):
date_value = form_data.get('date_to')
if isinstance(date_value, str):
date_value = dateutil.parser.parse(date_value).date()
qs = qs.filter(date__lte=date_value)
with tempfile.TemporaryDirectory() as d:
with ZipFile(os.path.join(d, 'tmp.zip'), 'w') as zipf:
for i in qs:
for i in self.event.invoices.all():
if not i.file:
invoice_pdf_task.apply(args=(i.pk,))
i.refresh_from_db()
@@ -46,44 +26,7 @@ class InvoiceExporter(BaseExporter):
i.file.close()
with open(os.path.join(d, 'tmp.zip'), 'rb') as zipf:
return '{}_invoices.zip'.format(self.event.slug), 'application/zip', zipf.read()
@property
def export_form_fields(self):
return OrderedDict(
[
('date_from',
forms.DateField(
label=_('Start date'),
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
required=False,
help_text=_('Only include invoices issued on or after this date. Note that the invoice date does '
'not always correspond to the order or payment date.')
)),
('date_to',
forms.DateField(
label=_('End date'),
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
required=False,
help_text=_('Only include invoices issued on or before this date. Note that the invoice date '
'does not always correspond to the order or payment date.')
)),
('payment_provider',
forms.ChoiceField(
label=_('Payment provider'),
choices=[
('', _('All payment providers')),
] + [
(k, v.verbose_name) for k, v in self.event.get_payment_providers().items()
],
required=False,
help_text=_('Only include invoices for orders that are currently set to this payment provider. '
'Note that this might include some invoices of other payment providers or misses '
'some invoices if the payment provider of an order has been changed and a new invoice '
'has been generated.')
)),
]
)
return 'invoices.zip', 'application/zip', zipf.read()
@receiver(register_data_exporters, dispatch_uid="exporter_invoices")

View File

@@ -101,7 +101,7 @@ class JSONExporter(BaseExporter):
}
}
return '{}_pretixdata.json'.format(self.event.slug), 'application/json', json.dumps(jo, cls=DjangoJSONEncoder)
return 'pretixdata.json', 'application/json', json.dumps(jo, cls=DjangoJSONEncoder)
@receiver(register_data_exporters, dispatch_uid="exporter_json")

View File

@@ -23,7 +23,7 @@ class MailExporter(BaseExporter):
).values('attendee_email')
data = "\r\n".join(set(a['email'] for a in addrs)
| set(a['attendee_email'] for a in pos if a['attendee_email']))
return '{}_pretixemails.txt'.format(self.event.slug), 'text/plain', data.encode("utf-8")
return 'pretixemails.txt', 'text/plain', data.encode("utf-8")
@property
def export_form_fields(self):

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