mirror of
https://github.com/pretix/pretix.git
synced 2026-11-12 01:37:07 +00:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
addf94afe2 | ||
|
|
0d828e439e | ||
|
|
69108a1c20 | ||
|
|
ceff9de5ee | ||
|
|
38b467cef7 | ||
|
|
ae95df7ea2 | ||
|
|
d362f16417 | ||
|
|
7f1a0fd58f | ||
|
|
6d05cfea8b | ||
|
|
c1b8259075 |
No files matched your search
@@ -56,7 +56,7 @@ calculated automatically.
|
||||
},
|
||||
"answers": [
|
||||
{
|
||||
"question": 1,
|
||||
"datafield": "A1B2C3D4",
|
||||
"answer": "23",
|
||||
"options": []
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
Cart positions
|
||||
==============
|
||||
|
||||
.. note::
|
||||
|
||||
This API is affected by an upcoming larger change to our API. The API documentation already reflects the future
|
||||
version. Features or fields not yet available are marked with **NEW**.
|
||||
The new version is expected to be released for hosted pretix early December and for self-hosted pretix with
|
||||
version 2027.1.
|
||||
You can find more information `on our blog <https://pretix.eu/about/en/blog/20261110-questionnaires/>`_.
|
||||
|
||||
The API provides limited access to the cart position data model. This API currently only allows creating and deleting
|
||||
cart positions to reserve quota.
|
||||
|
||||
@@ -31,12 +39,13 @@ voucher integer Internal ID of
|
||||
addon_to integer Internal ID of the position this position is an add-on for (or ``null``)
|
||||
is_bundled boolean If ``addon_to`` is set, this shows whether this is a bundled product or an addon product
|
||||
subevent integer ID of the date inside an event series this position belongs to (or ``null``)
|
||||
answers list of objects Answers to user-defined questions
|
||||
├ question integer Internal ID of the answered question
|
||||
answers list of objects Values of data fields (answers to user-defined questions)
|
||||
├ datafield string **NEW** The data field's ``identifier`` (internal reference)
|
||||
├ answer string Text representation of the answer
|
||||
├ question_identifier string The question's ``identifier`` field
|
||||
├ options list of integers Internal IDs of selected option(s)s (only for choice types)
|
||||
└ option_identifiers list of strings The ``identifier`` fields of the selected option(s)s
|
||||
├ option_identifiers list of strings The ``identifier`` fields of the selected option(s)s
|
||||
├ question integer **Deprecated.** Internal ID of the data field
|
||||
└ question_identifier string **Read-only, deprecated.** The data field's ``identifier`` (internal reference)
|
||||
seat objects The assigned seat (or ``null``)
|
||||
├ id integer Internal ID of the seat instance
|
||||
├ name string Human-readable seat name
|
||||
@@ -199,7 +208,8 @@ Cart position endpoints
|
||||
* ``bundled`` (optional, expect a list of nested objects of cart positions)
|
||||
* ``answers``
|
||||
|
||||
* ``question``
|
||||
* ``datafield`` **NEW**
|
||||
* ``question`` **DEPRECATED**
|
||||
* ``answer``
|
||||
* ``options``
|
||||
|
||||
@@ -223,7 +233,7 @@ Cart position endpoints
|
||||
"attendee_email": null,
|
||||
"answers": [
|
||||
{
|
||||
"question": 1,
|
||||
"datafield": "A1B2C3D4",
|
||||
"answer": "23",
|
||||
"options": []
|
||||
}
|
||||
@@ -285,7 +295,7 @@ Cart position endpoints
|
||||
"attendee_email": null,
|
||||
"answers": [
|
||||
{
|
||||
"question": 1,
|
||||
"datafield": "A1B2C3D4",
|
||||
"answer": "23",
|
||||
"options": []
|
||||
}
|
||||
@@ -303,7 +313,7 @@ Cart position endpoints
|
||||
"attendee_email": null,
|
||||
"answers": [
|
||||
{
|
||||
"question": 1,
|
||||
"datafield": "A1B2C3D4",
|
||||
"answer": "23",
|
||||
"options": []
|
||||
}
|
||||
|
||||
@@ -5,6 +5,14 @@
|
||||
Check-in
|
||||
========
|
||||
|
||||
.. note::
|
||||
|
||||
This API is affected by an upcoming larger change to our API. The API documentation already reflects the future
|
||||
version. Features or fields not yet available are marked with **NEW**.
|
||||
The new version is expected to be released for hosted pretix early December and for self-hosted pretix with
|
||||
version 2027.1.
|
||||
You can find more information `on our blog <https://pretix.eu/about/en/blog/20261110-questionnaires/>`_.
|
||||
|
||||
This page describes special APIs built for ticket scanning apps. For managing check-in configuration or other operations,
|
||||
please also see :ref:`rest-checkinlists`. The check-in list API also contains endpoints to obtain statistics or log
|
||||
failed scans.
|
||||
@@ -24,41 +32,43 @@ Checking a ticket in
|
||||
passed needs to be from a distinct event.
|
||||
|
||||
:query string expand: Expand a field inside the ``position`` object into a full object. Currently ``subevent``, ``item``, ``variation``, and ``answers.question`` are supported. Can be passed multiple times.
|
||||
:<json string secret: Scanned QR code corresponding to the ``secret`` attribute of a ticket.
|
||||
:<json string source_type: Type of source the ``secret`` was obtained form. Defaults to ``"barcode"``.
|
||||
:<json array lists: List of check-in list IDs to search on. No two check-in lists may be from the same event.
|
||||
:<json string type: Send ``"exit"`` for an exit and ``"entry"`` (default) for an entry.
|
||||
:<json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
|
||||
:<json boolean force: Specifies that the check-in should succeed regardless of revoked barcode, previous check-ins or required
|
||||
:>json string secret: Scanned QR code corresponding to the ``secret`` attribute of a ticket.
|
||||
:>json string source_type: Type of source the ``secret`` was obtained form. Defaults to ``"barcode"``.
|
||||
:>json array lists: List of check-in list IDs to search on. No two check-in lists may be from the same event.
|
||||
:>json string type: Send ``"exit"`` for an exit and ``"entry"`` (default) for an entry.
|
||||
:>json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
|
||||
:>json boolean force: Specifies that the check-in should succeed regardless of revoked barcode, previous check-ins or required
|
||||
questions that have not been filled. This is usually used to upload offline scans that already happened,
|
||||
because there's no point in validating them since they happened whether they are valid or not. Defaults to ``false``.
|
||||
:<json boolean questions_supported: When this parameter is set to ``true``, handling of questions is supported. If
|
||||
:>json boolean questions_supported: When this parameter is set to ``true``, handling of questions is supported. If
|
||||
you do not implement question handling in your user interface, you **must**
|
||||
set this to ``false``. In that case, questions will just be ignored. Defaults
|
||||
to ``true``.
|
||||
:<json boolean ignore_unpaid: Specifies that the check-in should succeed even if the order is in pending state.
|
||||
:>json boolean ignore_unpaid: Specifies that the check-in should succeed even if the order is in pending state.
|
||||
Defaults to ``false`` and only works when ``include_pending`` is set on the check-in
|
||||
list.
|
||||
:<json object answers: If questions are supported/required, you may/must supply a mapping of question IDs to their
|
||||
respective answers. The answers should always be strings. In case of (multiple-)choice-type
|
||||
:>json object answers: If questions are supported/required, you may/must supply a mapping of ``identifier`` (`internal
|
||||
reference`) of data fields to their respective answers (**NEW**).
|
||||
For backwards compatibility, a mapping of data field ``id`` to answer is supported, as well.
|
||||
The answers should always be strings. In case of (multiple-)choice-type
|
||||
answers, the string should contain the (comma-separated) IDs of the selected options.
|
||||
:<json string nonce: You can set this parameter to a unique random value to identify this check-in. If you're sending
|
||||
:>json string nonce: You can set this parameter to a unique random value to identify this check-in. If you're sending
|
||||
this request twice with the same nonce, the second request will also succeed but will always
|
||||
create only one check-in object even when the previous request was successful as well. This
|
||||
allows for a certain level of idempotency and enables you to re-try after a connection failure.
|
||||
:<json string exchange_medium_type: To perform an exchange to a reusable medium, pass the type of the new reusable medium
|
||||
:<json string exchange_medium_identifier: To perform an exchange to a reusable media, pass the identifier of the new medium
|
||||
:<json boolean use_order_locale: Specifies that pretix should use the customer's language (``locale`` field from the
|
||||
:>json string exchange_medium_type: To perform an exchange to a reusable medium, pass the type of the new reusable medium
|
||||
:>json string exchange_medium_identifier: To perform an exchange to a reusable media, pass the identifier of the new medium
|
||||
:>json boolean use_order_locale: Specifies that pretix should use the customer's language (``locale`` field from the
|
||||
order) when building texts (currently only the ``reason_explanation`` response field).
|
||||
Defaults to ``false`` in which case the server will determine the language (currently
|
||||
the event default language, might change in the future with support for the
|
||||
``Accept-Language`` header).
|
||||
:<json boolean simulate: Do not actually perform the check-in, only simulate the response. The ``position`` response
|
||||
:>json boolean simulate: Do not actually perform the check-in, only simulate the response. The ``position`` response
|
||||
object will not reflect the simulated changes.
|
||||
:>json string status: ``"ok"``, ``"incomplete"``, ``"exchange"``, or ``"error"``
|
||||
:>json string reason: Reason code, only set on status ``"error"``, see below for possible values.
|
||||
:>json string reason_explanation: Human-readable explanation, only set on status ``"error"`` and reason ``"rules"``, can be null.
|
||||
:>json object position: Copy of the matching order position (if any was found). The contents are the same as the
|
||||
:<json string status: ``"ok"``, ``"incomplete"``, ``"exchange"``, or ``"error"``
|
||||
:<json string reason: Reason code, only set on status ``"error"``, see below for possible values.
|
||||
:<json string reason_explanation: Human-readable explanation, only set on status ``"error"`` and reason ``"rules"``, can be null.
|
||||
:<json object position: Copy of the matching order position (if any was found). The contents are the same as the
|
||||
:ref:`order-position-resource`, with the following differences: (1) The ``checkins`` value
|
||||
will only include check-ins for the selected list. (2) An additional boolean property
|
||||
``require_attention`` will inform you whether either the order or the item have the
|
||||
@@ -66,13 +76,13 @@ Checking a ticket in
|
||||
back to values from a parent product or from invoice addresses. (4) Additional properties
|
||||
``order__status``, ``order__valid_if_pending``, ``order__require_approval``, and
|
||||
``order__locale`` are included with details form the order for convenience.
|
||||
:>json boolean require_attention: Whether or not the ``require_attention`` flag is set on the item or order.
|
||||
:>json list checkin_texts: List of additional texts to show to the user.
|
||||
:>json object list: Excerpt of information about the matching :ref:`check-in list <rest-checkinlists>` (if any was found),
|
||||
:<json boolean require_attention: Whether or not the ``require_attention`` flag is set on the item or order.
|
||||
:<json list checkin_texts: List of additional texts to show to the user.
|
||||
:<json object list: Excerpt of information about the matching :ref:`check-in list <rest-checkinlists>` (if any was found),
|
||||
including the attributes ``id``, ``name``, ``event``, ``subevent``, and ``include_pending``.
|
||||
:>json object questions: List of questions to be answered for check-in, only set on status ``"incomplete"``.
|
||||
:>json object media_policy: Reusable media policy (see documentation on items), only set on status ``"exchange"``.
|
||||
:>json object media_type: Reusable media type (see documentation on items), only set on status ``"exchange"``.
|
||||
:<json object questions: List of questions to be answered for check-in, only set on status ``"incomplete"``.
|
||||
:<json object media_policy: Reusable media policy (see documentation on items), only set on status ``"exchange"``.
|
||||
:<json object media_type: Reusable media type (see documentation on items), only set on status ``"exchange"``.
|
||||
|
||||
**Example request**:
|
||||
|
||||
@@ -92,7 +102,7 @@ Checking a ticket in
|
||||
"datetime": null,
|
||||
"questions_supported": true,
|
||||
"answers": {
|
||||
"4": "XS"
|
||||
"A1B2C3D4": "XS"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,6 +328,7 @@ Performing a ticket search
|
||||
],
|
||||
"answers": [
|
||||
{
|
||||
"datafield": "A1B2C3D4",
|
||||
"question": 12,
|
||||
"answer": "Foo",
|
||||
"options": []
|
||||
|
||||
@@ -5,6 +5,14 @@
|
||||
Check-in lists
|
||||
==============
|
||||
|
||||
.. note::
|
||||
|
||||
This API is affected by an upcoming larger change to our API. The API documentation already reflects the future
|
||||
version. Features or fields not yet available are marked with **NEW**.
|
||||
The new version is expected to be released for hosted pretix early December and for self-hosted pretix with
|
||||
version 2027.1.
|
||||
You can find more information `on our blog <https://pretix.eu/about/en/blog/20261110-questionnaires/>`_.
|
||||
|
||||
Resource description
|
||||
--------------------
|
||||
|
||||
@@ -569,6 +577,7 @@ Order position endpoints
|
||||
],
|
||||
"answers": [
|
||||
{
|
||||
"datafield": "A1B2C3D4",
|
||||
"question": 12,
|
||||
"answer": "Foo",
|
||||
"options": []
|
||||
@@ -609,27 +618,29 @@ Order position endpoints
|
||||
:query boolean untrusted_input: If set to true, the lookup parameter is **always** interpreted as a ``secret``, never
|
||||
as an ``id``. This should be always set if you are passing through untrusted, scanned
|
||||
data to avoid guessing of ticket IDs.
|
||||
:<json boolean questions_supported: When this parameter is set to ``true``, handling of questions is supported. If
|
||||
:>json boolean questions_supported: When this parameter is set to ``true``, handling of questions is supported. If
|
||||
you do not implement question handling in your user interface, you **must**
|
||||
set this to ``false``. In that case, questions will just be ignored. Defaults
|
||||
to ``true``.
|
||||
:<json boolean canceled_supported: When this parameter is set to ``true``, the response code ``canceled`` may be
|
||||
:>json boolean canceled_supported: When this parameter is set to ``true``, the response code ``canceled`` may be
|
||||
returned. Otherwise, canceled orders will return ``unpaid``. (**Deprecated**, in
|
||||
the future, this will be ignored and ``canceled`` may always be returned.)
|
||||
:<json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
|
||||
:<json boolean force: Specifies that the check-in should succeed regardless of revoked barcode, previous check-ins or required
|
||||
:>json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
|
||||
:>json boolean force: Specifies that the check-in should succeed regardless of revoked barcode, previous check-ins or required
|
||||
questions that have not been filled. This is usually used to upload offline scans that already happened,
|
||||
because there's no point in validating them since they happened whether they are valid or not. Defaults to ``false``.
|
||||
:<json string type: Send ``"exit"`` for an exit and ``"entry"`` (default) for an entry.
|
||||
:<json boolean ignore_unpaid: Specifies that the check-in should succeed even if the order is in pending state.
|
||||
:>json string type: Send ``"exit"`` for an exit and ``"entry"`` (default) for an entry.
|
||||
:>json boolean ignore_unpaid: Specifies that the check-in should succeed even if the order is in pending state.
|
||||
Defaults to ``false`` and only works when ``include_pending`` is set on the check-in
|
||||
list.
|
||||
:<json string nonce: You can set this parameter to a unique random value to identify this check-in. If you're sending
|
||||
:>json string nonce: You can set this parameter to a unique random value to identify this check-in. If you're sending
|
||||
this request twice with the same nonce, the second request will also succeed but will always
|
||||
create only one check-in object even when the previous request was successful as well. This
|
||||
allows for a certain level of idempotency and enables you to re-try after a connection failure.
|
||||
:<json object answers: If questions are supported/required, you may/must supply a mapping of question IDs to their
|
||||
respective answers. The answers should always be strings. In case of (multiple-)choice-type
|
||||
:>json object answers: If questions are supported/required, you may/must supply a mapping of ``identifier`` (`internal
|
||||
reference`) of data fields to their respective answers (**NEW**).
|
||||
For backwards compatibility, a mapping of data field ``id`` to answer is supported, as well.
|
||||
The answers should always be strings. In case of (multiple-)choice-type
|
||||
answers, the string should contain the (comma-separated) IDs of the selected options.
|
||||
|
||||
**Example request**:
|
||||
@@ -648,7 +659,7 @@ Order position endpoints
|
||||
"questions_supported": true,
|
||||
"canceled_supported": true,
|
||||
"answers": {
|
||||
"4": "XS"
|
||||
"A1B2C3D4": "XS"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
|
||||
.. _rest-datafieldoptions:
|
||||
|
||||
Data field options
|
||||
================
|
||||
|
||||
.. note::
|
||||
|
||||
This API is part of an upcoming larger change to our API. The API documentation already reflects the future
|
||||
version. Until the change, this resource is still available at ``/questions/(id)/options/``.
|
||||
The new version is expected to be released for hosted pretix early December and for self-hosted pretix with
|
||||
version 2027.1.
|
||||
You can find more information `on our blog <https://pretix.eu/about/en/blog/20261110-questionnaires/>`_.
|
||||
|
||||
Resource description
|
||||
--------------------
|
||||
|
||||
:ref:`Data fields <rest-datafields>` of type "choice" or "multiple choice" can have different options attached.
|
||||
|
||||
As with data fields, data field options exist on two resource paths, depending on whether
|
||||
the data field is attached to orders or order positions. Therefore, each endpoint below has two
|
||||
different URLs.
|
||||
|
||||
The options resource contains the following public fields:
|
||||
|
||||
.. rst-class:: rest-resource-table
|
||||
|
||||
===================================== ========================== =======================================================
|
||||
Field Type Description
|
||||
===================================== ========================== =======================================================
|
||||
id integer Internal ID of the option
|
||||
position integer An integer, used for sorting
|
||||
identifier string An arbitrary string that can be used for matching with
|
||||
other sources.
|
||||
answer multi-lingual string The displayed value of this option
|
||||
===================================== ========================== =======================================================
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/datafields/position/(datafield)/options/
|
||||
/api/v1/organizers/(organizer)/events/(event)/datafields/order/(datafield)/options/
|
||||
|
||||
Returns a list of all options for a given data field.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/datafields/position/11/options/ 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": 2,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 1,
|
||||
"answer": {"en": "S"}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"identifier": "DFEMJWMJ",
|
||||
"position": 2,
|
||||
"answer": {"en": "M"}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"identifier": "W9AH7RDE",
|
||||
"position": 3,
|
||||
"answer": {"en": "L"}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||
:query boolean active: If set to ``true`` or ``false``, only data fields with this value for the field ``active`` will be
|
||||
returned.
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:param datafield: The ``id`` field of the data field to fetch
|
||||
:statuscode 200: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event/data field does not exist **or** you have no permission to view this resource.
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/datafields/position/(datafield)/options/(id)/
|
||||
/api/v1/organizers/(organizer)/events/(event)/datafields/order/(datafield)/options/(id)/
|
||||
|
||||
Returns information on one option, identified by its ID.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/datafields/position/1/options/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,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 1,
|
||||
"answer": {"en": "S"}
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:param datafield: The ``id`` field of the data field to fetch
|
||||
:param id: The ``id`` field of the option 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)/datafields/position/(datafield)/options/
|
||||
/api/v1/organizers/(organizer)/events/(event)/datafields/order/(datafield)/options/
|
||||
|
||||
Create a new option
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1/organizers/bigevents/events/sampleconf/datafields/position/1/options/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 1,
|
||||
"answer": {"en": "S"}
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 1,
|
||||
"answer": {"en": "S"}
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer of the event/data field to create a option for
|
||||
:param event: The ``slug`` field of the event to create a option for
|
||||
:param datafield: The ``id`` field of the data field to create a option for
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: The option 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)/datafields/position/(datafield)/options/(id)/
|
||||
/api/v1/organizers/(organizer)/events/(event)/datafields/order/(datafield)/options/(id)/
|
||||
|
||||
Update an option. 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/datafields/position/1/options/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
Content-Length: 94
|
||||
|
||||
{
|
||||
"position": 3
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 1,
|
||||
"answer": {"en": "S"}
|
||||
}
|
||||
|
||||
: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 data field to modify
|
||||
:param id: The ``id`` field of the option to modify
|
||||
:statuscode 200: no error
|
||||
:statuscode 400: The option 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)/datafields/position/(datafield)/options/(id)/
|
||||
/api/v1/organizers/(organizer)/events/(event)/datafields/order/(datafield)/options/(id)/
|
||||
|
||||
Delete an option.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /api/v1/organizers/bigevents/events/sampleconf/datafields/position/1/options/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 datafield: The ``id`` field of the data field to modify
|
||||
:param id: The ``id`` field of the option 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.
|
||||
@@ -0,0 +1,428 @@
|
||||
|
||||
.. _rest-datafields:
|
||||
|
||||
Data fields
|
||||
=========
|
||||
|
||||
.. note::
|
||||
|
||||
This API is part of an upcoming larger change to our API. The API documentation already reflects the future
|
||||
version. Until the change, please keep using the endpoints at ``/questions/``.
|
||||
The new version is expected to be released for hosted pretix early December and for self-hosted pretix with
|
||||
version 2027.1.
|
||||
You can find more information `on our blog <https://pretix.eu/about/en/blog/20261110-questionnaires/>`_.
|
||||
|
||||
Resource description
|
||||
--------------------
|
||||
|
||||
Data fields define additional fields related to an order or an order position.
|
||||
By referencing data fields in a questionnaire, you can have customers or staff fill them out -- during or
|
||||
after checkout (web or POS), during check-in in the ticket scanner app, or in the backend.
|
||||
|
||||
There are two different resource paths for data fields, depending on whether the data field is attached
|
||||
to orders or order positions. Therefore, each endpoint below has two different URLs.
|
||||
|
||||
The data field resource contains the following public fields:
|
||||
|
||||
.. rst-class:: rest-resource-table
|
||||
|
||||
===================================== ========================== =======================================================
|
||||
Field Type Description
|
||||
===================================== ========================== =======================================================
|
||||
id integer Internal ID of the data field
|
||||
internal_name string **Required.** An internal field label
|
||||
identifier string An arbitrary string that can be used for matching with
|
||||
other sources.
|
||||
Must be unique per event, across all types of data fields.
|
||||
type string **Required.** The expected type of data. Valid options:
|
||||
|
||||
* ``N`` – number
|
||||
* ``S`` – one-line string
|
||||
* ``T`` – multi-line string
|
||||
* ``B`` – boolean
|
||||
* ``C`` – choice from a list
|
||||
* ``M`` – multiple choice from a list
|
||||
* ``F`` – file upload
|
||||
* ``D`` – date
|
||||
* ``H`` – time
|
||||
* ``W`` – date and time
|
||||
* ``CC`` – country code (ISO 3666-1 alpha-2)
|
||||
* ``TEL`` – telephone number
|
||||
show_during_checkin boolean If ``true``, the value of the data field will be shown
|
||||
during check-in (if the check-in client supports it).
|
||||
*Only supported on per-position data fields.*
|
||||
print_on_invoice boolean If ``true``, the data field will be shown on
|
||||
invoices.
|
||||
*Only supported on per-position data fields.*
|
||||
print_on_invoice_label multi-lingual string **Required if ``print_on_invoice`` is ``true``.** Label
|
||||
to print on the invoice before the value of this field.
|
||||
Otherwise `null`.
|
||||
options list of objects In case of data field type ``C`` or ``M``, this lists the
|
||||
available objects. Only writable during creation,
|
||||
use :ref:`separate endpoint <rest-datafieldoptions>` to modify this later.
|
||||
├ id integer Internal ID of the option
|
||||
├ position integer An integer, used for sorting
|
||||
├ identifier string An arbitrary string that can be used for matching with
|
||||
other sources.
|
||||
└ answer multi-lingual string The displayed value of this option
|
||||
valid_number_min string Minimum value for number fields (optional)
|
||||
valid_number_max string Maximum value for number fields (optional)
|
||||
valid_date_min date Minimum value for date fields (optional)
|
||||
valid_date_max date Maximum value for date fields (optional)
|
||||
valid_datetime_min datetime Minimum value for date and time fields (optional)
|
||||
valid_datetime_max datetime Maximum value for date and time fields (optional)
|
||||
valid_file_portrait boolean Turn on file validation for portrait photos
|
||||
valid_string_length_min integer Minimum length for string fields (optional)
|
||||
valid_string_length_max integer Maximum length for string fields (optional)
|
||||
referenced_by_questionnaires list of integers **Read-only.** List of questionnaire IDs that reference
|
||||
this data field
|
||||
===================================== ========================== =======================================================
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/datafields/position/
|
||||
/api/v1/organizers/(organizer)/events/(event)/datafields/order/
|
||||
|
||||
Returns a list of all data fields within a given event.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/datafields/position/ 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,
|
||||
"internal_name": "T-Shirt size",
|
||||
"type": "C",
|
||||
"identifier": "WY3TP9SL",
|
||||
"show_during_checkin": false,
|
||||
"print_on_invoice": false,
|
||||
"print_on_invoice_label": null,
|
||||
"valid_number_min": null,
|
||||
"valid_number_max": null,
|
||||
"valid_date_min": null,
|
||||
"valid_date_max": null,
|
||||
"valid_datetime_min": null,
|
||||
"valid_datetime_max": null,
|
||||
"valid_string_length_min": null,
|
||||
"valid_string_length_max": null,
|
||||
"valid_file_portrait": false,
|
||||
"options": [
|
||||
{
|
||||
"id": 1,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 0,
|
||||
"answer": {"en": "S"}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"identifier": "DFEMJWMJ",
|
||||
"position": 1,
|
||||
"answer": {"en": "M"}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"identifier": "W9AH7RDE",
|
||||
"position": 2,
|
||||
"answer": {"en": "L"}
|
||||
}
|
||||
],
|
||||
"referenced_by_questionnaires": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``id`` and ``position``.
|
||||
Default: ``position``
|
||||
:query string identifier: Only return questions with the given identifier string
|
||||
:query boolean ask_during_checkin: Only return questions that are or are not to be asked during check-in
|
||||
:query boolean required: Only return questions that are or are not required to fill in
|
||||
: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)/datafields/position/(id)/
|
||||
/api/v1/organizers/(organizer)/events/(event)/datafields/order/(id)/
|
||||
|
||||
Returns information on one data field, identified by its ID.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/datafields/position/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,
|
||||
"internal_name": "T-Shirt size",
|
||||
"type": "C",
|
||||
"identifier": "WY3TP9SL",
|
||||
"show_during_checkin": false,
|
||||
"print_on_invoice": false,
|
||||
"print_on_invoice_label": null,
|
||||
"valid_number_min": null,
|
||||
"valid_number_max": null,
|
||||
"valid_date_min": null,
|
||||
"valid_date_max": null,
|
||||
"valid_datetime_min": null,
|
||||
"valid_datetime_max": null,
|
||||
"valid_string_length_min": null,
|
||||
"valid_string_length_max": null,
|
||||
"valid_file_portrait": false,
|
||||
"options": [
|
||||
{
|
||||
"id": 1,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 0,
|
||||
"answer": {"en": "S"}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"identifier": "DFEMJWMJ",
|
||||
"position": 1,
|
||||
"answer": {"en": "M"}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"identifier": "W9AH7RDE",
|
||||
"position": 2,
|
||||
"answer": {"en": "L"}
|
||||
}
|
||||
],
|
||||
"referenced_by_questionnaires": [1]
|
||||
}
|
||||
|
||||
: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 question 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)/datafields/position/
|
||||
/api/v1/organizers/(organizer)/events/(event)/datafields/order/
|
||||
|
||||
Create a new question
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1/organizers/bigevents/events/sampleconf/datafields/position/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"internal_name": "T-Shirt size",
|
||||
"type": "C",
|
||||
"show_during_checkin": false,
|
||||
"print_on_invoice": false,
|
||||
"print_on_invoice_label": null,
|
||||
"options": [
|
||||
{
|
||||
"answer": {"en": "S"}
|
||||
},
|
||||
{
|
||||
"answer": {"en": "M"}
|
||||
},
|
||||
{
|
||||
"answer": {"en": "L"}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"internal_name": "T-Shirt size",
|
||||
"type": "C",
|
||||
"identifier": "WY3TP9SL",
|
||||
"show_during_checkin": false,
|
||||
"print_on_invoice": false,
|
||||
"print_on_invoice_label": null,
|
||||
"valid_number_min": null,
|
||||
"valid_number_max": null,
|
||||
"valid_date_min": null,
|
||||
"valid_date_max": null,
|
||||
"valid_datetime_min": null,
|
||||
"valid_datetime_max": null,
|
||||
"valid_string_length_min": null,
|
||||
"valid_string_length_max": null,
|
||||
"valid_file_portrait": false,
|
||||
"options": [
|
||||
{
|
||||
"id": 1,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 0,
|
||||
"answer": {"en": "S"}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"identifier": "DFEMJWMJ",
|
||||
"position": 1,
|
||||
"answer": {"en": "M"}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"identifier": "W9AH7RDE",
|
||||
"position": 2,
|
||||
"answer": {"en": "L"}
|
||||
}
|
||||
],
|
||||
"referenced_by_questionnaires": [1]
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer of the event to create an item for
|
||||
:param event: The ``slug`` field of the event to create an item for
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: The item could not be created due to invalid submitted data.
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
|
||||
|
||||
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/datafields/position/(id)/
|
||||
/api/v1/organizers/(organizer)/events/(event)/datafields/order/(id)/
|
||||
|
||||
Update a data field. 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 ``options`` field. If
|
||||
you need to update/delete options please use the nested dedicated endpoints.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PATCH /api/v1/organizers/bigevents/events/sampleconf/datafields/position/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
Content-Length: 94
|
||||
|
||||
{
|
||||
"internal_name": "Clothing size"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"internal_name": "Clothing size",
|
||||
"type": "C",
|
||||
"identifier": "WY3TP9SL",
|
||||
"show_during_checkin": false,
|
||||
"print_on_invoice": false,
|
||||
"print_on_invoice_label": null,
|
||||
"valid_number_min": null,
|
||||
"valid_number_max": null,
|
||||
"valid_date_min": null,
|
||||
"valid_date_max": null,
|
||||
"valid_datetime_min": null,
|
||||
"valid_datetime_max": null,
|
||||
"valid_string_length_min": null,
|
||||
"valid_string_length_max": null,
|
||||
"valid_file_portrait": false,
|
||||
"options": [
|
||||
{
|
||||
"id": 1,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 0,
|
||||
"answer": {"en": "S"}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"identifier": "DFEMJWMJ",
|
||||
"position": 1,
|
||||
"answer": {"en": "M"}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"identifier": "W9AH7RDE",
|
||||
"position": 2,
|
||||
"answer": {"en": "L"}
|
||||
}
|
||||
],
|
||||
"referenced_by_questionnaires": [1]
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to modify
|
||||
:param event: The ``slug`` field of the event to modify
|
||||
:param id: The ``id`` field of the question to modify
|
||||
:statuscode 200: no error
|
||||
:statuscode 400: The question 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)/datafields/position/(id)/
|
||||
/api/v1/organizers/(organizer)/events/(event)/datafields/order/(id)/
|
||||
|
||||
Delete a data field.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /api/v1/organizers/bigevents/events/sampleconf/datafields/position/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 question 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.
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ at :ref:`plugin-docs`.
|
||||
item_add-ons
|
||||
item_meta_properties
|
||||
item_program_times
|
||||
datafields
|
||||
datafield_options
|
||||
questionnaires
|
||||
questions
|
||||
question_options
|
||||
quotas
|
||||
|
||||
@@ -37,12 +37,9 @@ tax_rule integer The internal
|
||||
admission boolean ``true`` for items that grant admission to the event
|
||||
(such as primary tickets) and ``false`` for others
|
||||
(such as add-ons or merchandise).
|
||||
personalized boolean ``true`` for items that require personalization according
|
||||
personalized boolean **DEPRECATED.** ``true`` for items that require personalization according
|
||||
to event settings. Only affects system-level fields, not
|
||||
custom questions. Currently only allowed for products with
|
||||
``admission`` set to ``true``. For backwards compatibility,
|
||||
when creating new items and this field is not given, it defaults
|
||||
to the same value as ``admission``.
|
||||
custom questions. Defaults to ``false``.
|
||||
position integer An integer, used for sorting
|
||||
picture file A product picture to be displayed in the shop
|
||||
(can be ``null``).
|
||||
@@ -233,6 +230,17 @@ meta_data object Values set fo
|
||||
|
||||
The ``program_times`` attribute has been added.
|
||||
|
||||
.. versionchanged:: 2027.1
|
||||
|
||||
The ``personalized`` attribute has been deprecated.
|
||||
|
||||
For compatibility with existing applications:
|
||||
|
||||
* When retrieving products, `personalized` is `True` if any attached questionnaire asks for `attendee_name` and/or `attendee_email`.
|
||||
* When creating a new product, setting the `personalized` field to `True` will attach the "Attendee data" questionnaire to the new product. If no such questionnaire exists, it will be created.
|
||||
Before, the `personalized` field defaulted to the same value as `admission`. Now, it defaults to `false`.
|
||||
* When updating a product, the `personalized` field cannot be changed, returning an HTTP 400 error if attempted.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
|
||||
@@ -9,6 +9,14 @@
|
||||
Orders
|
||||
======
|
||||
|
||||
.. note::
|
||||
|
||||
This API is affected by an upcoming larger change to our API. The API documentation already reflects the future
|
||||
version. Features or fields not yet available are marked with **NEW**.
|
||||
The new version is expected to be released for hosted pretix early December and for self-hosted pretix with
|
||||
version 2027.1.
|
||||
You can find more information `on our blog <https://pretix.eu/about/en/blog/20261110-questionnaires/>`_.
|
||||
|
||||
Order resource
|
||||
--------------
|
||||
|
||||
@@ -119,6 +127,11 @@ cancellation_date datetime Time of order c
|
||||
plugin_data object Additional data added by plugins.
|
||||
use_gift_cards list of strings List of unique gift card secrets that are used to pay
|
||||
for this order.
|
||||
answers list of objects **NEW** Values of data fields (answers to user-defined questions)
|
||||
├ datafield string The data field's ``identifier`` (internal reference)
|
||||
├ answer string Text representation of the answer
|
||||
├ options list of integers Internal IDs of selected option(s)s (only for choice types)
|
||||
└ option_identifiers list of strings The ``identifier`` fields of the selected option(s)s
|
||||
===================================== ========================== =======================================================
|
||||
|
||||
|
||||
@@ -162,6 +175,10 @@ use_gift_cards list of strings List of unique
|
||||
|
||||
The ``use_gift_cards`` attribute has been added.
|
||||
|
||||
.. versionchanged:: 2027.1
|
||||
|
||||
The ``answers`` attribute has been added.
|
||||
|
||||
.. _order-position-resource:
|
||||
|
||||
Order position resource
|
||||
@@ -231,12 +248,13 @@ print_logs list of objects List of print j
|
||||
downloads list of objects List of ticket download options
|
||||
├ output string Ticket output provider (e.g. ``pdf``, ``passbook``)
|
||||
└ url string Download URL
|
||||
answers list of objects Answers to user-defined questions
|
||||
├ question integer Internal ID of the answered question
|
||||
├ answer string Text representation of the answer (URL if answer is a file)
|
||||
├ question_identifier string The question's ``identifier`` field
|
||||
answers list of objects Values of data fields (answers to user-defined questions)
|
||||
├ datafield string **NEW** The data field's ``identifier`` (internal reference)
|
||||
├ answer string Text representation of the answer
|
||||
├ options list of integers Internal IDs of selected option(s)s (only for choice types)
|
||||
└ option_identifiers list of strings The ``identifier`` fields of the selected option(s)s
|
||||
├ option_identifiers list of strings The ``identifier`` fields of the selected option(s)s
|
||||
├ question integer **Deprecated.** Internal ID of the data field
|
||||
└ question_identifier string **Read-only, deprecated.** The data field's ``identifier`` (internal reference)
|
||||
seat objects The assigned seat. Can be ``null``.
|
||||
├ id integer Internal ID of the seat instance
|
||||
├ name string Human-readable seat name
|
||||
@@ -452,10 +470,11 @@ List of all orders
|
||||
],
|
||||
"answers": [
|
||||
{
|
||||
"datafield": "WY3TP9SL",
|
||||
"question": 12,
|
||||
"question_identifier": "WY3TP9SL",
|
||||
"answer": "Foo",
|
||||
"option_idenfiters": [],
|
||||
"option_identifiers": [],
|
||||
"options": []
|
||||
}
|
||||
],
|
||||
@@ -488,7 +507,15 @@ List of all orders
|
||||
],
|
||||
"refunds": [],
|
||||
"cancellation_date": null,
|
||||
"plugin_data": {}
|
||||
"plugin_data": {},
|
||||
"answers": [
|
||||
{
|
||||
"datafield": "XYZ12345",
|
||||
"answer": "42",
|
||||
"option_identifiers": [],
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -699,6 +726,7 @@ Fetching individual orders
|
||||
],
|
||||
"answers": [
|
||||
{
|
||||
"datafield": "WY3TP9SL",
|
||||
"question": 12,
|
||||
"question_identifier": "WY3TP9SL",
|
||||
"answer": "Foo",
|
||||
@@ -735,7 +763,15 @@ Fetching individual orders
|
||||
],
|
||||
"refunds": [],
|
||||
"cancellation_date": null,
|
||||
"plugin_data": {}
|
||||
"plugin_data": {},
|
||||
"answers": [
|
||||
{
|
||||
"datafield": "XYZ12345",
|
||||
"answer": "42",
|
||||
"option_identifiers": [],
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
@@ -1079,7 +1115,8 @@ Creating orders
|
||||
* ``discount`` (optional, only possible if ``price`` is set; attention: if this is set to not-``null`` on any position, automatic calculation of discounts will not run)
|
||||
* ``answers``
|
||||
|
||||
* ``question``
|
||||
* ``datafield`` (**NEW**)
|
||||
* ``question`` (**DEPRECATED**)
|
||||
* ``answer``
|
||||
* ``options``
|
||||
|
||||
@@ -1114,6 +1151,12 @@ Creating orders
|
||||
order isn't completely paid after its creation with ``use_gift_cards``, then a subsequent request to the payment
|
||||
endpoint is needed.
|
||||
|
||||
* **NEW** ``answers`` (optional). Values for order-level data fields.
|
||||
|
||||
* ``datafield``
|
||||
* ``answer``
|
||||
* ``options``
|
||||
|
||||
If you want to use add-on products, you need to set the ``positionid`` fields of all positions manually
|
||||
to incrementing integers starting with ``1``. Then, you can reference one of these
|
||||
IDs in the ``addon_to`` field of another position. Note that all add_ons for a specific position need to come
|
||||
@@ -1161,6 +1204,12 @@ Creating orders
|
||||
"internal_reference": "",
|
||||
"vat_id": ""
|
||||
},
|
||||
"answers": [
|
||||
{
|
||||
"datafield": "XYZ12345",
|
||||
"answer": "42"
|
||||
}
|
||||
],
|
||||
"positions": [
|
||||
{
|
||||
"positionid": 1,
|
||||
@@ -1174,7 +1223,7 @@ Creating orders
|
||||
"addon_to": null,
|
||||
"answers": [
|
||||
{
|
||||
"question": 1,
|
||||
"datafield": "WY3TP9SL",
|
||||
"answer": "23",
|
||||
"options": []
|
||||
}
|
||||
@@ -1684,6 +1733,7 @@ List of all order positions
|
||||
],
|
||||
"answers": [
|
||||
{
|
||||
"datafield": "WY3TP9SL",
|
||||
"question": 12,
|
||||
"question_identifier": "WY3TP9SL",
|
||||
"answer": "Foo",
|
||||
@@ -1863,6 +1913,7 @@ Fetching individual positions
|
||||
],
|
||||
"answers": [
|
||||
{
|
||||
"datafield": "WY3TP9SL",
|
||||
"question": 12,
|
||||
"question_identifier": "WY3TP9SL",
|
||||
"answer": "Foo",
|
||||
@@ -1971,7 +2022,7 @@ Manipulating individual positions
|
||||
|
||||
* ``answers``: If specified, you will need to provide **all** answers for this order position.
|
||||
Validation is handled the same way as when creating orders through the API. You are therefore
|
||||
expected to provide ``question``, ``answer``, and possibly ``options``. ``question_identifier``
|
||||
expected to provide ``datafield``, ``answer``, and possibly ``options``. ``question`` is deprecated. ``question_identifier``
|
||||
and ``option_identifiers`` will be ignored. As a special case, you can submit the magic value
|
||||
``"file:keep"`` as the answer to a file question to keep the current value without re-uploading it.
|
||||
|
||||
@@ -2067,7 +2118,7 @@ Manipulating individual positions
|
||||
* ``state``
|
||||
|
||||
* ``answers``: Validation is handled the same way as when creating orders through the API. You are therefore
|
||||
expected to provide ``question``, ``answer``, and possibly ``options``. ``question_identifier``
|
||||
expected to provide ``datafield``, ``answer``, and possibly ``options``. ``question`` is deprecated. ``question_identifier``
|
||||
and ``option_identifiers`` will be ignored. As a special case, you can submit the magic value
|
||||
``"file:keep"`` as the answer to a file question to keep the current value without re-uploading it.
|
||||
|
||||
|
||||
@@ -1,229 +1,7 @@
|
||||
Question options
|
||||
================
|
||||
|
||||
Resource description
|
||||
--------------------
|
||||
.. warning:: This resource is **deprecated**.
|
||||
|
||||
Questions of type "choice" or "multiple choice" can have different options attached.
|
||||
The options resource contains the following public fields:
|
||||
New applications must use the :ref:`rest-datafieldoptions` resource.
|
||||
|
||||
.. rst-class:: rest-resource-table
|
||||
|
||||
===================================== ========================== =======================================================
|
||||
Field Type Description
|
||||
===================================== ========================== =======================================================
|
||||
id integer Internal ID of the option
|
||||
position integer An integer, used for sorting
|
||||
identifier string An arbitrary string that can be used for matching with
|
||||
other sources.
|
||||
answer multi-lingual string The displayed value of this option
|
||||
===================================== ========================== =======================================================
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/questions/(question)/options/
|
||||
|
||||
Returns a list of all options for a given question.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/questions/11/options/ 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": 2,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 1,
|
||||
"answer": {"en": "S"}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"identifier": "DFEMJWMJ",
|
||||
"position": 2,
|
||||
"answer": {"en": "M"}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"identifier": "W9AH7RDE",
|
||||
"position": 3,
|
||||
"answer": {"en": "L"}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||
:query boolean active: If set to ``true`` or ``false``, only questions with this value for the field ``active`` will be
|
||||
returned.
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:param question: The ``id`` field of the question to fetch
|
||||
:statuscode 200: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event/question does not exist **or** you have no permission to view this resource.
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/questions/(question)/options/(id)/
|
||||
|
||||
Returns information on one option, identified by its ID.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/questions/1/options/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,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 1,
|
||||
"answer": {"en": "S"}
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:param question: The ``id`` field of the question to fetch
|
||||
:param id: The ``id`` field of the option 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)/questions/(question)/options/
|
||||
|
||||
Creates a new option
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1/organizers/bigevents/events/sampleconf/questions/1/options/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 1,
|
||||
"answer": {"en": "S"}
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 1,
|
||||
"answer": {"en": "S"}
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer of the event/question to create a option for
|
||||
:param event: The ``slug`` field of the event to create a option for
|
||||
:param question: The ``id`` field of the question to create a option for
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: The option 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)/questions/(question)/options/(id)/
|
||||
|
||||
Update an option. 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/questions/1/options/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
Content-Length: 94
|
||||
|
||||
{
|
||||
"position": 3
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"identifier": "LVETRWVU",
|
||||
"position": 1,
|
||||
"answer": {"en": "S"}
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to modify
|
||||
:param event: The ``slug`` field of the event to modify
|
||||
:param id: The ``id`` field of the question to modify
|
||||
:param id: The ``id`` field of the option to modify
|
||||
:statuscode 200: no error
|
||||
:statuscode 400: The option 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)/questions/(id)/options/(id)/
|
||||
|
||||
Delete an option.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /api/v1/organizers/bigevents/events/sampleconf/questions/1/options/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 question to modify
|
||||
:param id: The ``id`` field of the option 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.
|
||||
@@ -0,0 +1,379 @@
|
||||
|
||||
.. _rest-questionnaires:
|
||||
|
||||
Questionnaires
|
||||
=========
|
||||
|
||||
.. note::
|
||||
|
||||
This API is part of an upcoming larger change to our API. The API documentation already reflects the future
|
||||
version. Until the change, please keep using the endpoints at ``/questions/``.
|
||||
The new version is expected to be released for hosted pretix early December and for self-hosted pretix with
|
||||
version 2027.1.
|
||||
You can find more information `on our blog <https://pretix.eu/about/en/blog/20261110-questionnaires/>`_.
|
||||
|
||||
Questionnaires are attached to one or more products, and a product can have multiple questionnaires.
|
||||
They consist of questionnaire rows, each of which can be one of the following:
|
||||
|
||||
Reference to system field
|
||||
To reference a system field, use one of the identifier strings from the table below in the row's `datafield` attribute.
|
||||
Currently, system fields are only defined on order positions, not on orders.
|
||||
|
||||
=============== =========================== ============================
|
||||
Container Identifier Label
|
||||
=============== =========================== ============================
|
||||
Order position `"@attendee_name_parts"` Attendee name
|
||||
Order position `"@attendee_email"` Attendee email
|
||||
Order position `"@company"` Company
|
||||
Order position `"@street"` Street
|
||||
Order position `"@zipcode"` ZIP code
|
||||
Order position `"@city"` City
|
||||
Order position `"@state"` State
|
||||
Order position `"@country"` Country
|
||||
=============== =========================== ============================
|
||||
|
||||
|
||||
Reference to user-defined data field
|
||||
To reference a :ref:`user-defined data field <rest-datafields>`, use its internal reference (`identifier`)
|
||||
in the row's `datafield` attribute.
|
||||
|
||||
|
||||
Static explanation text / subheading
|
||||
To add a static text block, set the row's `datafield` attribute to `null`. In this case, the text
|
||||
from the `label` attribute is displayed as a subheading, and the `help_text` attribute as a normal paragraph.
|
||||
|
||||
|
||||
Resource description
|
||||
--------------------
|
||||
|
||||
|
||||
.. rst-class:: rest-resource-table
|
||||
|
||||
===================================== ========================== =======================================================
|
||||
Field Type Description
|
||||
===================================== ========================== =======================================================
|
||||
id integer Internal ID of the questionnaire
|
||||
type string The type of questionnaire. Valid options:
|
||||
|
||||
* ``order_purchase`` — Order-wide, before purchase
|
||||
* ``position_purchase`` — Per product, before purchase
|
||||
* ``position_attendee`` — Per product, via attendee link
|
||||
* ``position_checkin`` — Per product, at check-in
|
||||
* ``position_hidden`` — Per product, hidden
|
||||
internal_name string Internal label for the questionnaire.
|
||||
items list of integers List of item IDs this questionnaire is assigned to.
|
||||
*Only supported on per-product questionnaires.*
|
||||
position integer An integer, used for sorting
|
||||
all_sales_channels boolean If ``true`` (default), the questions from this questionnaire
|
||||
are asked on all sales channels.
|
||||
limit_sales_channels list of strings List of sales channel identifiers the questions are asked on
|
||||
if ``all_sales_channels`` is ``false``.
|
||||
rows list of objects List of all questionnaire rows.
|
||||
├ datafield string Reference to a data field (see above)
|
||||
├ required boolean If ``true``, the question needs to be filled out.
|
||||
├ label multi-lingual string The field label shown to the customer. In case of a static
|
||||
text block, the subheading.
|
||||
├ help_text multi-lingual string The help text shown to the customer.
|
||||
├ dependency_datafield string ``datafield`` reference of a different row in the same
|
||||
questionnaire.
|
||||
The current question will only be shown if the question
|
||||
given in this attribute is set to one of the values given
|
||||
in ``dependency_values``.
|
||||
├ dependency_values list of strings If ``dependency_datafield`` is set to reference a boolean
|
||||
data field, this should be ``["True"]`` or ``["False"]``.
|
||||
Otherwise, it should be a list of ``identifier`` values
|
||||
of question options.
|
||||
├ rendered_help_text string **Read-only.** Markdown contents from help_text rendered
|
||||
to HTML
|
||||
===================================== ========================== =======================================================
|
||||
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/questionnaires/
|
||||
|
||||
Returns a list of all questionnaires within a given event.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/questionnaires/ 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": 2,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "position_purchase",
|
||||
"internal_name": "Attendee data",
|
||||
"items": [1, 2],
|
||||
"position": 0,
|
||||
"all_sales_channels": true,
|
||||
"limit_sales_channels": [],
|
||||
"rows": [
|
||||
{
|
||||
"datafield": "@attendee_name_parts",
|
||||
"required": false,
|
||||
"label": {"en": "Attendee name"},
|
||||
"help_text": {},
|
||||
"dependency_datafield": null,
|
||||
"dependency_values": [],
|
||||
"rendered_help_text": ""
|
||||
},
|
||||
{
|
||||
"datafield": "@attendee_email",
|
||||
"required": false,
|
||||
"label": {"en": "Attendee email"},
|
||||
"help_text": {"en": "*We'll send you our program guide a few days before the event.*"},
|
||||
"dependency_datafield": null,
|
||||
"dependency_values": [],
|
||||
"rendered_help_text": "<em>We'll send you our program guide a few days before the event.</em>"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "order_purchase",
|
||||
"internal_name": "Per-order questions",
|
||||
"items": [],
|
||||
"position": 0,
|
||||
"all_sales_channels": true,
|
||||
"limit_sales_channels": [],
|
||||
"rows": [
|
||||
{
|
||||
"datafield": "87Y8DCS9",
|
||||
"required": false,
|
||||
"label": {"en": "How did you find us?"},
|
||||
"help_text": {},
|
||||
"dependency_datafield": null,
|
||||
"dependency_values": [],
|
||||
"rendered_help_text": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``id`` and ``position``.
|
||||
Default: ``position``
|
||||
:query string type: Only return questionnaires with the given type
|
||||
:query string items: Only return questionnaires associated with the given item (internal item ID)
|
||||
:query boolean sales_channel: Only return questionnaires visible on the given sales channel (sales channel identifier string)
|
||||
: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)/questionnaires/(id)/
|
||||
|
||||
Returns information on one questionnaire, identified by its ID.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/events/sampleconf/questionnaires/2/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 2,
|
||||
"type": "order_purchase",
|
||||
"internal_name": "Per-order questions",
|
||||
"items": [],
|
||||
"position": 0,
|
||||
"all_sales_channels": true,
|
||||
"limit_sales_channels": [],
|
||||
"rows": [
|
||||
{
|
||||
"datafield": "87Y8DCS9",
|
||||
"required": false,
|
||||
"label": {"en": "How did you find us?"},
|
||||
"help_text": {},
|
||||
"dependency_datafield": null,
|
||||
"dependency_values": [],
|
||||
"rendered_help_text": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
: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 questionnaire 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)/questionnaires/
|
||||
|
||||
Create a new questionnaire
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1/organizers/bigevents/events/sampleconf/questionnaires/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"type": "order_purchase",
|
||||
"internal_name": "Per-order questions",
|
||||
"position": 0,
|
||||
"all_sales_channels": true,
|
||||
"rows": [
|
||||
{
|
||||
"datafield": "87Y8DCS9",
|
||||
"required": false,
|
||||
"label": {"en": "How did you find us?"}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 2,
|
||||
"type": "order_purchase",
|
||||
"internal_name": "Per-order questions",
|
||||
"items": [],
|
||||
"position": 0,
|
||||
"all_sales_channels": true,
|
||||
"limit_sales_channels": [],
|
||||
"rows": [
|
||||
{
|
||||
"datafield": "87Y8DCS9",
|
||||
"required": false,
|
||||
"label": {"en": "How did you find us?"},
|
||||
"help_text": {},
|
||||
"dependency_datafield": null,
|
||||
"dependency_values": [],
|
||||
"rendered_help_text": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer of the event to create a questionnaire for
|
||||
:param event: The ``slug`` field of the event to create a questionnaire for
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: The questionnaire 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)/questionnaires/(id)/
|
||||
|
||||
Update a questionnaire. 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 only change all ``rows`` en bloc, or keep all of them unchanged in case of a ``PATCH``. If you
|
||||
change ``rows``, you need to specify all fields of the resource.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PATCH /api/v1/organizers/bigevents/events/sampleconf/questionnaires/2/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"internal_name": "Questions asked per order"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 2,
|
||||
"type": "order_purchase",
|
||||
"internal_name": "Questions asked per order",
|
||||
"items": [],
|
||||
"position": 0,
|
||||
"all_sales_channels": true,
|
||||
"limit_sales_channels": [],
|
||||
"rows": [
|
||||
{
|
||||
"datafield": "87Y8DCS9",
|
||||
"required": false,
|
||||
"label": {"en": "How did you find us?"},
|
||||
"help_text": {},
|
||||
"dependency_datafield": null,
|
||||
"dependency_values": [],
|
||||
"rendered_help_text": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer of the questionnaire to modify
|
||||
:param event: The ``slug`` field of the event of the questionnaire to modify
|
||||
:param id: The ``id`` field of the questionnaire to modify
|
||||
:statuscode 200: no error
|
||||
:statuscode 400: The questionnaire 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)/questionnaires/(id)/
|
||||
|
||||
Delete a questionnaire.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /api/v1/organizers/bigevents/events/sampleconf/questionnaires/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 of the questionnaire to modify
|
||||
:param event: The ``slug`` field of the event of the questionnaire to modify
|
||||
:param id: The ``id`` field of the questionnaire 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.
|
||||
@@ -8,10 +8,23 @@
|
||||
Questions
|
||||
=========
|
||||
|
||||
.. warning:: This resource is **deprecated**. It is only provided for compatibility with existing applications
|
||||
and should not be used in new development.
|
||||
|
||||
New applications must use the :ref:`rest-datafields` and :ref:`rest-questionnaires` resources.
|
||||
The new version is expected to be released for hosted pretix early December and for self-hosted pretix with
|
||||
version 2027.1.
|
||||
You can find more information `on our blog <https://pretix.eu/about/en/blog/20261110-questionnaires/>`_.
|
||||
|
||||
This resource will continue to work in read-only mode on events where the
|
||||
:ref:`questionnaire configuration <rest-questionnaires>` is compatible with the old data model.
|
||||
If incompatible configuration is detected, all endpoints return HTTP 410.
|
||||
|
||||
|
||||
Resource description
|
||||
--------------------
|
||||
|
||||
Questions define additional fields that need to be filled out by customers during checkout.
|
||||
Questions define additional fields related to an order position that need to be filled out by customers during checkout.
|
||||
The question resource contains the following public fields:
|
||||
|
||||
.. rst-class:: rest-resource-table
|
||||
@@ -84,6 +97,10 @@ dependency_value string An old version
|
||||
|
||||
The ``show_during_checkin`` attribute has been added.
|
||||
|
||||
.. versionchanged:: 2026.[TODO]
|
||||
|
||||
The questions resource was deprecated and replaced by the :ref:`rest-datafields` and :ref:`rest-questionnaires` resources.
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
@@ -249,6 +266,13 @@ Endpoints
|
||||
|
||||
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/questions/
|
||||
|
||||
.. warning:: This endpoint is **deprecated** and will be removed as part of an upcoming API change.
|
||||
The new version is expected to be released for hosted pretix early December and for self-hosted
|
||||
pretix with version 2027.1.
|
||||
You can find more information `on our blog <https://pretix.eu/about/en/blog/20261110-questionnaires/>`_.
|
||||
After the change, instead create a :ref:`data field <rest-datafields>` first, then add
|
||||
it to a :ref:`questionnaire <rest-questionnaires>`.
|
||||
|
||||
Creates a new question
|
||||
|
||||
**Example request**:
|
||||
@@ -351,6 +375,13 @@ Endpoints
|
||||
|
||||
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/questions/(id)/
|
||||
|
||||
.. warning:: This endpoint is **deprecated** and will be removed as part of an upcoming API change.
|
||||
The new version is expected to be released for hosted pretix early December and for self-hosted
|
||||
pretix with version 2027.1.
|
||||
You can find more information `on our blog <https://pretix.eu/about/en/blog/20261110-questionnaires/>`_.
|
||||
After the change, instead update the relevant :ref:`data field <rest-datafields>`, or
|
||||
the reference to it inside a :ref:`questionnaire <rest-questionnaires>`.
|
||||
|
||||
Update a question. 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.
|
||||
@@ -437,6 +468,14 @@ Endpoints
|
||||
|
||||
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/questions/(id)/
|
||||
|
||||
|
||||
.. warning:: This endpoint is **deprecated** and will be removed as part of an upcoming API change.
|
||||
The new version is expected to be released for hosted pretix early December and for self-hosted
|
||||
pretix with version 2027.1.
|
||||
You can find more information `on our blog <https://pretix.eu/about/en/blog/20261110-questionnaires/>`_.
|
||||
After the change, insteadremove the questionnaire child from its :ref:`questionnaire <rest-questionnaires>`,
|
||||
or delete the relevant :ref:`data field <rest-datafields>`.
|
||||
|
||||
Delete a question.
|
||||
|
||||
**Example request**:
|
||||
|
||||
@@ -1282,7 +1282,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
raise ValidationError(
|
||||
'Orders cannot have more than %(max)s positions.' % {'max': settings.PRETIX_MAX_ORDER_SIZE}
|
||||
)
|
||||
errs = [{} for p in data]
|
||||
errs = defaultdict(dict)
|
||||
if any([p.get('positionid') for p in data]):
|
||||
if not all([p.get('positionid') for p in data]):
|
||||
for i, p in enumerate(data):
|
||||
@@ -1311,16 +1311,16 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
last_posid = p['positionid']
|
||||
|
||||
elif any([p.get('addon_to') for p in data]):
|
||||
errs = [
|
||||
{'positionid': ["If you set addon_to on any position, you need to specify position IDs manually."]}
|
||||
for p in data
|
||||
]
|
||||
errs = {
|
||||
i: {'positionid': ["If you set addon_to on any position, you need to specify position IDs manually."]}
|
||||
for i, p in enumerate(data)
|
||||
}
|
||||
else:
|
||||
for i, p in enumerate(data):
|
||||
p['positionid'] = i + 1
|
||||
|
||||
if any(errs):
|
||||
raise ValidationError(errs)
|
||||
if errs:
|
||||
raise ValidationError(dict(errs))
|
||||
return data
|
||||
|
||||
def create(self, validated_data):
|
||||
@@ -1437,7 +1437,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
# use further down
|
||||
del quota_diff_for_locking, voucher_diff_for_locking, seat_diff_for_locking
|
||||
|
||||
errs = [{} for p in positions_data]
|
||||
errs = defaultdict(dict)
|
||||
|
||||
for i, pos_data in enumerate(positions_data):
|
||||
if pos_data.get('voucher'):
|
||||
@@ -1566,7 +1566,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
)
|
||||
]
|
||||
|
||||
if any(errs):
|
||||
if errs:
|
||||
raise ValidationError({'positions': errs})
|
||||
|
||||
if validated_data.get('locale', None) is None:
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from collections import defaultdict
|
||||
from decimal import Decimal
|
||||
|
||||
from rest_framework import serializers
|
||||
@@ -38,23 +39,20 @@ class VoucherListSerializer(serializers.ListSerializer):
|
||||
|
||||
codes = set()
|
||||
seats = set()
|
||||
errs = []
|
||||
err = False
|
||||
for voucher_data in validated_data:
|
||||
errs = defaultdict(dict)
|
||||
for i, voucher_data in enumerate(validated_data):
|
||||
if voucher_data.get('seat') and (voucher_data.get('seat'), voucher_data.get('subevent')) in seats:
|
||||
err = True
|
||||
errs.append({'code': ['Duplicate seat ID in request.']})
|
||||
errs[i] = {'code': ['Duplicate seat ID in request.']}
|
||||
continue
|
||||
else:
|
||||
seats.add((voucher_data.get('seat'), voucher_data.get('subevent')))
|
||||
|
||||
if voucher_data['code'] in codes:
|
||||
err = True
|
||||
errs.append({'code': ['Duplicate voucher code in request.']})
|
||||
errs[i] = {'code': ['Duplicate voucher code in request.']}
|
||||
else:
|
||||
codes.add(voucher_data['code'])
|
||||
errs.append({})
|
||||
if err:
|
||||
|
||||
if errs:
|
||||
raise ValidationError(errs)
|
||||
return super().create(validated_data)
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ from typing import List, Optional, Protocol
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import DatabaseError, transaction
|
||||
from django.utils.functional import classproperty
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
@@ -101,9 +102,8 @@ class OutboundSyncProvider:
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.close()
|
||||
|
||||
@classmethod
|
||||
@property
|
||||
def display_name(cls):
|
||||
@classproperty
|
||||
def display_name(cls): # noqa: N805
|
||||
return str(cls.identifier)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -196,6 +196,14 @@ OPTIONS = OrderedDict([
|
||||
'offsets': [46 * mm, 46 * mm],
|
||||
'pagesize': pagesizes.A4,
|
||||
}),
|
||||
('herma_75x40', {
|
||||
'name': 'HERMA 75 x 40 mm (9010)',
|
||||
'cols': 2,
|
||||
'rows': 6,
|
||||
'margins': [28.5 * mm, 25 * mm, 28.5 * mm, 25 * mm],
|
||||
'offsets': [85 * mm, 40 * mm],
|
||||
'pagesize': pagesizes.A4,
|
||||
}),
|
||||
('herma_88.9x33.87', {
|
||||
'name': 'HERMA 88,9 x 33,87 mm (4515)',
|
||||
'cols': 2,
|
||||
|
||||
@@ -219,6 +219,11 @@ TEMPLATES = {
|
||||
"pagesize": (75 * mm, 52 * mm),
|
||||
"layout": _simple_template(75 * mm, 52 * mm),
|
||||
},
|
||||
"75x40": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=75, height=40),
|
||||
"pagesize": (75 * mm, 40 * mm),
|
||||
"layout": _simple_template(75 * mm, 40 * mm),
|
||||
},
|
||||
"70x36": {
|
||||
"label": format_lazy(_("{width} x {height} mm label"), width=70, height=36),
|
||||
"pagesize": (70 * mm, 36 * mm),
|
||||
|
||||
@@ -123,6 +123,7 @@ class TicketLayoutViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = TicketLayoutSerializer
|
||||
queryset = TicketLayout.objects.none()
|
||||
lookup_field = 'id'
|
||||
write_permission = 'event.settings.general:write'
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.event.ticket_layouts.all()
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
{% for provider in request.organizer.sso_providers.all %}
|
||||
{% if provider.is_active %}
|
||||
<a href="{% eventurl request.organizer "presale:organizer.customer.login" provider=provider.pk %}?next={% if request.domain_mode == 'event' or request.domain_mode == 'organizer_alternative' %}{{ request.scheme }}://{{ request.get_host }}{% endif %}{{ request.get_full_path|urlencode }}"
|
||||
<a href="{% abseventurl request.organizer "presale:organizer.customer.login" provider=provider.pk %}?next={% if request.domain_mode == 'event' or request.domain_mode == 'organizer_alternative' %}{{ request.scheme }}://{{ request.get_host }}{% endif %}{{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-primary btn-lg btn-block" data-open-in-popup-window>
|
||||
{{ provider.button_label }}
|
||||
</a>
|
||||
|
||||
@@ -76,7 +76,7 @@ from pretix.base.models.tax import TaxedPrice
|
||||
from pretix.base.payment import PaymentException
|
||||
from pretix.base.services.invoices import (
|
||||
generate_cancellation, generate_invoice, invoice_pdf, invoice_pdf_task,
|
||||
invoice_qualified,
|
||||
invoice_qualified, transmit_invoice,
|
||||
)
|
||||
from pretix.base.services.orders import (
|
||||
OrderChangeManager, OrderError, _try_auto_refund, cancel_order,
|
||||
@@ -907,6 +907,9 @@ class OrderModify(EventViewMixin, OrderDetailMixin, OrderQuestionsViewMixin, Tem
|
||||
self.order.log_action('pretix.event.order.invoice.reissued', data={
|
||||
'invoice': inv.pk
|
||||
})
|
||||
transmit_invoice.apply_async(args=(c.event_id, c.pk, True))
|
||||
if c.pk != inv.pk:
|
||||
transmit_invoice.apply_async(args=(inv.event_id, inv.pk, True))
|
||||
messages.success(self.request, _('The invoice has been reissued.'))
|
||||
except Exception as e:
|
||||
self.order.log_action("pretix.event.order.invoice.failed", data={
|
||||
|
||||
@@ -507,7 +507,6 @@ REST_FRAMEWORK = {
|
||||
],
|
||||
'EXCEPTION_HANDLER': 'pretix.api.exception.custom_exception_handler',
|
||||
'UNICODE_JSON': False,
|
||||
'LIST_SERIALIZER_ERRORS_AS_DICT': False
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -57,7 +57,6 @@ filterwarnings =
|
||||
ignore:teardown:pytest.PytestWarning
|
||||
ignore:avoid running initialization queries:RuntimeWarning
|
||||
ignore:.*The 'strip_cdata' option of HTMLParser.*:DeprecationWarning:
|
||||
ignore:The list-based error format for `ListSerializer`:rest_framework.deprecation.RemovedInDRF320Warning
|
||||
|
||||
|
||||
[coverage:run]
|
||||
|
||||
+16
-13
@@ -424,7 +424,7 @@ def test_cartpos_create_answer_validation(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'answers': [{'question': ['The specified question does not belong to this event.']}]}
|
||||
assert resp.data == {'answers': {0: {'question': ['The specified question does not belong to this event.']}}}
|
||||
|
||||
res['answers'][0]['question'] = question.pk
|
||||
res['answers'][0]['options'] = [question.options.first().pk]
|
||||
@@ -434,8 +434,9 @@ def test_cartpos_create_answer_validation(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'answers': [{'non_field_errors': ['You should not specify options if the question is not of a choice type.']}]}
|
||||
assert resp.data == {'answers': {
|
||||
0: {'non_field_errors': ['You should not specify options if the question is not of a choice type.']}
|
||||
}}
|
||||
|
||||
question.type = Question.TYPE_CHOICE
|
||||
question.save()
|
||||
@@ -447,7 +448,7 @@ def test_cartpos_create_answer_validation(token_client, organizer, event, item,
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'answers': [{'non_field_errors': ['You need to specify options if the question is of a choice type.']}]}
|
||||
'answers': {0: {'non_field_errors': ['You need to specify options if the question is of a choice type.']}}}
|
||||
|
||||
with scopes_disabled():
|
||||
question.options.create(answer="L")
|
||||
@@ -461,7 +462,8 @@ def test_cartpos_create_answer_validation(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'answers': [{'non_field_errors': ['You can specify at most one option for this question.']}]}
|
||||
assert resp.data == {
|
||||
'answers': {0: {'non_field_errors': ['You can specify at most one option for this question.']}}}
|
||||
|
||||
r = token_client.post(
|
||||
'/api/v1/upload',
|
||||
@@ -533,7 +535,7 @@ def test_cartpos_create_answer_validation(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'answers': [{'non_field_errors': ['A valid number is required.']}]}
|
||||
assert resp.data == {'answers': {0: {'non_field_errors': ['A valid number is required.']}}}
|
||||
|
||||
question.type = Question.TYPE_BOOLEAN
|
||||
question.save()
|
||||
@@ -573,7 +575,8 @@ def test_cartpos_create_answer_validation(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'answers': [{'non_field_errors': ['Please specify "true" or "false" for boolean questions.']}]}
|
||||
assert resp.data == {
|
||||
'answers': {0: {'non_field_errors': ['Please specify "true" or "false" for boolean questions.']}}}
|
||||
|
||||
question.type = Question.TYPE_DATE
|
||||
question.save()
|
||||
@@ -599,7 +602,7 @@ def test_cartpos_create_answer_validation(token_client, organizer, event, item,
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'answers': [{'non_field_errors': ['Date has wrong format. Use one of these formats instead: YYYY-MM-DD.']}]}
|
||||
'answers': {0: {'non_field_errors': ['Date has wrong format. Use one of these formats instead: YYYY-MM-DD.']}}}
|
||||
|
||||
question.type = Question.TYPE_DATETIME
|
||||
question.save()
|
||||
@@ -624,9 +627,9 @@ def test_cartpos_create_answer_validation(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'answers': [{'non_field_errors': [
|
||||
assert resp.data == {'answers': {0: {'non_field_errors': [
|
||||
'Datetime has wrong format. Use one of these formats instead: '
|
||||
'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].']}]}
|
||||
'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].']}}}
|
||||
|
||||
question.type = Question.TYPE_TIME
|
||||
question.save()
|
||||
@@ -651,8 +654,8 @@ def test_cartpos_create_answer_validation(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'answers': [
|
||||
{'non_field_errors': ['Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]].']}]}
|
||||
assert resp.data == {'answers': {0: {'non_field_errors': [
|
||||
'Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]].']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -1387,7 +1390,7 @@ def test_cartpos_create_bulk_validation_error_in_addon(token_client, organizer,
|
||||
'data': None,
|
||||
'success': False,
|
||||
'errors': {
|
||||
'addons': [{'item': ['Invalid pk "-1" - object does not exist.']}]
|
||||
'addons': {0: {'item': ['Invalid pk "-1" - object does not exist.']}}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -958,9 +958,9 @@ def test_item_create_with_addon(token_client, organizer, event, item, category,
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() in [
|
||||
'{"addons":["The minimum count needs to be equal to or greater than zero."]}',
|
||||
'{"addons":[{"min_count":["Ensure this value is greater than or equal to 0."]}]}',
|
||||
assert resp.data in [
|
||||
{"addons": {0: "The minimum count needs to be equal to or greater than zero."}},
|
||||
{"addons": {0: {"min_count": ["Ensure this value is greater than or equal to 0."]}}},
|
||||
]
|
||||
with scopes_disabled():
|
||||
assert 2 == Item.objects.all().count()
|
||||
|
||||
@@ -978,7 +978,7 @@ def test_order_create_position_secret_optional(token_client, organizer, event, i
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
|
||||
assert resp.data == {'positions': [{'secret': ['You cannot assign a position secret that already exists.']}]}
|
||||
assert resp.data == {'positions': {0: {'secret': ['You cannot assign a position secret that already exists.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -1024,7 +1024,7 @@ def test_order_create_fee_type_validation(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'fees': [{'fee_type': ['"unknown" is not a valid choice.']}]}
|
||||
assert resp.data == {'fees': {0: {'fee_type': ['"unknown" is not a valid choice.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -1143,7 +1143,7 @@ def test_order_create_tax_rule_wrong_event(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'fees': [{'tax_rule': ['The specified tax rate does not belong to this event.']}]}
|
||||
assert resp.data == {'fees': {0: {'tax_rule': ['The specified tax rate does not belong to this event.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -1158,7 +1158,7 @@ def test_order_create_subevent_not_allowed(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'subevent': ['You cannot set a subevent for this event.']}]}
|
||||
assert resp.data == {'positions': {0: {'subevent': ['You cannot set a subevent for this event.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -1185,7 +1185,7 @@ def test_order_create_subevent_validation(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'subevent': ['You need to set a subevent.']}]}
|
||||
assert resp.data == {'positions': {0: {'subevent': ['You need to set a subevent.']}}}
|
||||
|
||||
res['positions'][0]['subevent'] = subevent2.pk
|
||||
resp = token_client.post(
|
||||
@@ -1194,7 +1194,7 @@ def test_order_create_subevent_validation(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'subevent': ['The specified subevent does not belong to this event.']}]}
|
||||
assert resp.data == {'positions': {0: {'subevent': ['The specified subevent does not belong to this event.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -1210,7 +1210,7 @@ def test_order_create_item_validation(token_client, organizer, event, item, item
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'item': ['The specified item is not active.']}]}
|
||||
assert resp.data == {'positions': {0: {'item': ['The specified item is not active.']}}}
|
||||
item.active = True
|
||||
item.save()
|
||||
|
||||
@@ -1222,7 +1222,7 @@ def test_order_create_item_validation(token_client, organizer, event, item, item
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'item': ['The specified item does not belong to this event.']}]}
|
||||
assert resp.data == {'positions': {0: {'item': ['The specified item does not belong to this event.']}}}
|
||||
|
||||
with scopes_disabled():
|
||||
var2 = item2.variations.create(value="A")
|
||||
@@ -1236,7 +1236,7 @@ def test_order_create_item_validation(token_client, organizer, event, item, item
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'variation': ['You cannot specify a variation for this item.']}]}
|
||||
assert resp.data == {'positions': {0: {'variation': ['You cannot specify a variation for this item.']}}}
|
||||
|
||||
with scopes_disabled():
|
||||
var1 = item.variations.create(value="A")
|
||||
@@ -1248,7 +1248,7 @@ def test_order_create_item_validation(token_client, organizer, event, item, item
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'item': ['The product "Budget Ticket" is not assigned to a quota.']}]}
|
||||
assert resp.data == {'positions': {0: {'item': ['The product "Budget Ticket" is not assigned to a quota.']}}}
|
||||
|
||||
with scopes_disabled():
|
||||
quota.variations.add(var1)
|
||||
@@ -1267,7 +1267,7 @@ def test_order_create_item_validation(token_client, organizer, event, item, item
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [{'variation': ['The specified variation does not belong to the specified item.']}]}
|
||||
'positions': {0: {'variation': ['The specified variation does not belong to the specified item.']}}}
|
||||
|
||||
res['positions'][0]['variation'] = None
|
||||
resp = token_client.post(
|
||||
@@ -1276,7 +1276,7 @@ def test_order_create_item_validation(token_client, organizer, event, item, item
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'variation': ['You should specify a variation for this item.']}]}
|
||||
assert resp.data == {'positions': {0: {'variation': ['You should specify a variation for this item.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -1294,7 +1294,7 @@ def test_order_create_subevent_disabled(token_client, organizer, event, item, su
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'item': ['The product "Budget Ticket" is not available on this date.']}]}
|
||||
assert resp.data == {'positions': {0: {'item': ['The product "Budget Ticket" is not available on this date.']}}}
|
||||
|
||||
s.delete()
|
||||
resp = token_client.post(
|
||||
@@ -1326,7 +1326,7 @@ def test_order_create_subevent_variation_disabled(token_client, organizer, event
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'item': ['The product "Budget Ticket" is not available on this date.']}]}
|
||||
assert resp.data == {'positions': {0: {'item': ['The product "Budget Ticket" is not available on this date.']}}}
|
||||
|
||||
s.delete()
|
||||
resp = token_client.post(
|
||||
@@ -1453,16 +1453,15 @@ def test_order_create_positionid_validation(token_client, organizer, event, item
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{},
|
||||
{
|
||||
'positions': {
|
||||
1: {
|
||||
'addon_to': [
|
||||
'If you set addon_to, you need to make sure that the '
|
||||
'referenced position ID exists and is transmitted directly '
|
||||
'before its add-ons.'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
res['positions'] = [
|
||||
@@ -1493,10 +1492,14 @@ def test_order_create_positionid_validation(token_client, organizer, event, item
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [
|
||||
{'positionid': ["If you set addon_to on any position, you need to specify position IDs manually."]},
|
||||
{'positionid': ["If you set addon_to on any position, you need to specify position IDs manually."]}
|
||||
]}
|
||||
assert resp.data == {'positions': {
|
||||
0:
|
||||
{'positionid': [
|
||||
"If you set addon_to on any position, you need to specify position IDs manually."]},
|
||||
1:
|
||||
{'positionid': [
|
||||
"If you set addon_to on any position, you need to specify position IDs manually."]}
|
||||
}}
|
||||
|
||||
res['positions'] = [
|
||||
{
|
||||
@@ -1526,12 +1529,11 @@ def test_order_create_positionid_validation(token_client, organizer, event, item
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{},
|
||||
{
|
||||
'positions': {
|
||||
1: {
|
||||
'positionid': ['If you set position IDs manually, you need to do so for all positions.']
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
res['positions'] = [
|
||||
@@ -1563,12 +1565,11 @@ def test_order_create_positionid_validation(token_client, organizer, event, item
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{},
|
||||
{
|
||||
'positions': {
|
||||
1: {
|
||||
'positionid': ['Position IDs need to be consecutive.']
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
res['positions'] = [
|
||||
@@ -1615,7 +1616,7 @@ def test_order_create_answer_validation(token_client, organizer, event, item, qu
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [{'answers': [{'question': ['The specified question does not belong to this event.']}]}]}
|
||||
'positions': {0: {'answers': {0: {'question': ['The specified question does not belong to this event.']}}}}}
|
||||
|
||||
res['positions'][0]['answers'][0]['question'] = question.pk
|
||||
res['positions'][0]['answers'][0]['options'] = [question.options.first().pk]
|
||||
@@ -1625,8 +1626,8 @@ def test_order_create_answer_validation(token_client, organizer, event, item, qu
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'answers': [
|
||||
{'non_field_errors': ['You should not specify options if the question is not of a choice type.']}]}]}
|
||||
assert resp.data == {'positions': {0: {'answers': {
|
||||
0: {'non_field_errors': ['You should not specify options if the question is not of a choice type.']}}}}}
|
||||
|
||||
question.type = Question.TYPE_CHOICE
|
||||
question.save()
|
||||
@@ -1637,8 +1638,8 @@ def test_order_create_answer_validation(token_client, organizer, event, item, qu
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [
|
||||
{'answers': [{'non_field_errors': ['You need to specify options if the question is of a choice type.']}]}]}
|
||||
assert resp.data == {'positions': {0: {
|
||||
'answers': {0: {'non_field_errors': ['You need to specify options if the question is of a choice type.']}}}}}
|
||||
|
||||
with scopes_disabled():
|
||||
question2.options.create(answer="L")
|
||||
@@ -1653,7 +1654,8 @@ def test_order_create_answer_validation(token_client, organizer, event, item, qu
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [{'answers': [{'non_field_errors': ['The specified option does not belong to this question.']}]}]}
|
||||
'positions': {
|
||||
0: {'answers': {0: {'non_field_errors': ['The specified option does not belong to this question.']}}}}}
|
||||
|
||||
with scopes_disabled():
|
||||
question.options.create(answer="L")
|
||||
@@ -1669,7 +1671,8 @@ def test_order_create_answer_validation(token_client, organizer, event, item, qu
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [{'answers': [{'non_field_errors': ['You can specify at most one option for this question.']}]}]}
|
||||
'positions': {
|
||||
0: {'answers': {0: {'non_field_errors': ['You can specify at most one option for this question.']}}}}}
|
||||
|
||||
r = token_client.post(
|
||||
'/api/v1/upload',
|
||||
@@ -1745,7 +1748,7 @@ def test_order_create_answer_validation(token_client, organizer, event, item, qu
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'answers': [{'non_field_errors': ['A valid number is required.']}]}]}
|
||||
assert resp.data == {'positions': {0: {'answers': {0: {'non_field_errors': ['A valid number is required.']}}}}}
|
||||
|
||||
question.type = Question.TYPE_BOOLEAN
|
||||
question.save()
|
||||
@@ -1787,8 +1790,8 @@ def test_order_create_answer_validation(token_client, organizer, event, item, qu
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [{'answers': [{'non_field_errors': ['Please specify "true" or "false" for boolean questions.']}]}]}
|
||||
assert resp.data == {'positions': {
|
||||
0: {'answers': {0: {'non_field_errors': ['Please specify "true" or "false" for boolean questions.']}}}}}
|
||||
|
||||
question.type = Question.TYPE_DATE
|
||||
question.save()
|
||||
@@ -1814,8 +1817,8 @@ def test_order_create_answer_validation(token_client, organizer, event, item, qu
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'answers': [
|
||||
{'non_field_errors': ['Date has wrong format. Use one of these formats instead: YYYY-MM-DD.']}]}]}
|
||||
assert resp.data == {'positions': {0: {'answers': {
|
||||
0: {'non_field_errors': ['Date has wrong format. Use one of these formats instead: YYYY-MM-DD.']}}}}}
|
||||
|
||||
question.type = Question.TYPE_DATETIME
|
||||
question.save()
|
||||
@@ -1841,9 +1844,9 @@ def test_order_create_answer_validation(token_client, organizer, event, item, qu
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'answers': [{'non_field_errors': [
|
||||
assert resp.data == {'positions': {0: {'answers': {0: {'non_field_errors': [
|
||||
'Datetime has wrong format. Use one of these formats instead: '
|
||||
'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].']}]}]}
|
||||
'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].']}}}}}
|
||||
|
||||
question.type = Question.TYPE_TIME
|
||||
question.save()
|
||||
@@ -1869,8 +1872,8 @@ def test_order_create_answer_validation(token_client, organizer, event, item, qu
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'answers': [
|
||||
{'non_field_errors': ['Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]].']}]}]}
|
||||
assert resp.data == {'positions': {0: {'answers': {
|
||||
0: {'non_field_errors': ['Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]].']}}}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -1910,10 +1913,10 @@ def test_order_create_quota_validation(token_client, organizer, event, item, quo
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']},
|
||||
{'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']},
|
||||
]
|
||||
'positions': {
|
||||
0: {'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']},
|
||||
1: {'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']},
|
||||
}
|
||||
}
|
||||
|
||||
quota.size = 1
|
||||
@@ -1924,12 +1927,8 @@ def test_order_create_quota_validation(token_client, organizer, event, item, quo
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{},
|
||||
{'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']},
|
||||
]
|
||||
}
|
||||
assert resp.data == {'positions': {
|
||||
1: {'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']}}}
|
||||
|
||||
res['force'] = True
|
||||
resp = token_client.post(
|
||||
@@ -1961,11 +1960,8 @@ def test_order_create_quota_consume_cart(token_client, organizer, event, item, q
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']},
|
||||
]
|
||||
}
|
||||
assert resp.data == {'positions': {
|
||||
0: {'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']}}}
|
||||
|
||||
res['consume_carts'] = [cr.cart_id]
|
||||
resp = token_client.post(
|
||||
@@ -2000,11 +1996,8 @@ def test_order_create_quota_consume_cart_expired(token_client, organizer, event,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']},
|
||||
]
|
||||
}
|
||||
assert resp.data == {'positions': {
|
||||
0: {'item': ['There is not enough quota available on quota "Budget Quota" to perform the operation.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -2176,11 +2169,7 @@ def test_order_create_with_blocked_seat(token_client, organizer, event, item, qu
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'seat': ['The selected seat "Seat A1" is not available.']},
|
||||
]
|
||||
}
|
||||
assert resp.data == {'positions': {0: {'seat': ['The selected seat "Seat A1" is not available.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -2199,11 +2188,7 @@ def test_order_create_with_used_seat(token_client, organizer, event, item, quota
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'seat': ['The selected seat "Seat A1" is not available.']},
|
||||
]
|
||||
}
|
||||
assert resp.data == {'positions': {0: {'seat': ['The selected seat "Seat A1" is not available.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -2218,11 +2203,7 @@ def test_order_create_with_unknown_seat(token_client, organizer, event, item, qu
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'seat': ['The specified seat does not exist.']},
|
||||
]
|
||||
}
|
||||
assert resp.data == {'positions': {0: {'seat': ['The specified seat does not exist.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -2236,11 +2217,7 @@ def test_order_create_require_seat(token_client, organizer, event, item, quota,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'seat': ['The specified product requires to choose a seat.']},
|
||||
]
|
||||
}
|
||||
assert resp.data == {'positions': {0: {'seat': ['The specified product requires to choose a seat.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -2258,11 +2235,7 @@ def test_order_create_unseated(token_client, organizer, event, item, quota, seat
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'seat': ['The specified product does not allow to choose a seat.']},
|
||||
]
|
||||
}
|
||||
assert resp.data == {'positions': {0: {'seat': ['The specified product does not allow to choose a seat.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -2300,12 +2273,7 @@ def test_order_create_with_duplicate_seat(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{},
|
||||
{'seat': ['The selected seat "Seat A1" is not available.']},
|
||||
]
|
||||
}
|
||||
assert resp.data == {'positions': {1: {'seat': ['The selected seat "Seat A1" is not available.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -2810,8 +2778,8 @@ def test_order_create_autopricing_voucher_budget_full(token_client, organizer, e
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{}, {'voucher': ['The voucher has a remaining budget of 0.00, therefore a '
|
||||
'discount of 1.50 can not be given.']}]}
|
||||
assert resp.data == {'positions': {1: {'voucher': ['The voucher has a remaining budget of 0.00, therefore a '
|
||||
'discount of 1.50 can not be given.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -2832,8 +2800,8 @@ def test_order_create_voucher_budget_exceeded(token_client, organizer, event, it
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'positions': [{'voucher': ['The voucher has a remaining budget of 3.00, therefore a '
|
||||
'discount of 4.00 can not be given.']}]}
|
||||
assert resp.data == {'positions': {0: {'voucher': ['The voucher has a remaining budget of 3.00, therefore a '
|
||||
'discount of 4.00 can not be given.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -2876,11 +2844,7 @@ def test_order_create_voucher_unknown_code(token_client, organizer, event, item,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'voucher': ['Object with code=FOOBAR does not exist.']},
|
||||
]
|
||||
}
|
||||
assert resp.data == {'positions': {0: {'voucher': ['Object with code=FOOBAR does not exist.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -2899,9 +2863,7 @@ def test_order_create_voucher_redeemed(token_client, organizer, event, item, quo
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'voucher': ['The voucher has already been used the maximum number of times.']},
|
||||
]
|
||||
'positions': {0: {'voucher': ['The voucher has already been used the maximum number of times.']}}
|
||||
}
|
||||
|
||||
|
||||
@@ -2924,11 +2886,10 @@ def test_order_create_voucher_redeemed_partially(token_client, organizer, event,
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{},
|
||||
{'voucher': ['The voucher has already been used the maximum number of times.']},
|
||||
{'voucher': ['The voucher has already been used the maximum number of times.']},
|
||||
]
|
||||
'positions': {
|
||||
1: {'voucher': ['The voucher has already been used the maximum number of times.']},
|
||||
2: {'voucher': ['The voucher has already been used the maximum number of times.']},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2949,10 +2910,7 @@ def test_order_create_voucher_item_mismatch(token_client, organizer, event, item
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'voucher': ['This voucher is not valid for this product.']},
|
||||
]
|
||||
}
|
||||
'positions': {0: {'voucher': ['This voucher is not valid for this product.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -2972,10 +2930,7 @@ def test_order_create_voucher_expired(token_client, organizer, event, item, quot
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{'voucher': ['This voucher is expired.']},
|
||||
]
|
||||
}
|
||||
'positions': {0: {'voucher': ['This voucher is expired.']}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -3103,10 +3058,8 @@ def test_create_cart_and_consume_cart_with_addons(token_client, organizer, event
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'positions': [
|
||||
{},
|
||||
{'item': ['There is not enough quota available on quota "Addon Quota" to perform the operation.']},
|
||||
]
|
||||
'positions': {
|
||||
1: {'item': ['There is not enough quota available on quota "Addon Quota" to perform the operation.']}}
|
||||
}
|
||||
|
||||
res['consume_carts'] = ['aaa@api']
|
||||
@@ -3214,12 +3167,7 @@ def test_order_create_use_medium_other_organizer(token_client, organizer, event,
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.data == {
|
||||
"positions": [
|
||||
{
|
||||
"use_reusable_medium": ["The specified medium does not belong to this organizer."]
|
||||
}
|
||||
]
|
||||
}
|
||||
"positions": {0: {"use_reusable_medium": ["The specified medium does not belong to this organizer."]}}}
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
@@ -3361,14 +3309,8 @@ def test_order_create_auto_pricing_explicit_discount_not_allowed(token_client, o
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
"positions": [
|
||||
{},
|
||||
{
|
||||
"discount": ["You can only specify a discount if you do the price computation, but price is not set."]
|
||||
}
|
||||
]
|
||||
}
|
||||
assert resp.data == {"positions": {
|
||||
1: {"discount": ["You can only specify a discount if you do the price computation, but price is not set."]}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
||||
@@ -409,7 +409,7 @@ def test_subevent_create(team, token_client, organizer, event, subevent, meta_pr
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"item_price_overrides":[{"item":["Invalid pk \\"555\\" - object does not exist."]}]}'
|
||||
assert resp.data == {"item_price_overrides": {0: {"item": ["Invalid pk \"555\" - object does not exist."]}}}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -580,7 +580,7 @@ def test_subevent_update(token_client, organizer, event, subevent, item, item2,
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"item_price_overrides":[{"item":["Invalid pk \\"123\\" - object does not exist."]}]}'
|
||||
assert resp.data == {"item_price_overrides": {0: {"item": ["Invalid pk \"123\" - object does not exist."]}}}
|
||||
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/subevents/{}/'.format(organizer.slug, event.slug, subevent.pk),
|
||||
@@ -686,7 +686,8 @@ def test_subevent_update(token_client, organizer, event, subevent, item, item2,
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.content.decode() == '{"variation_price_overrides":[{"variation":["Invalid pk \\"123\\" - object does not exist."]}]}'
|
||||
assert resp.data == {
|
||||
"variation_price_overrides": {0: {"variation": ["Invalid pk \"123\" - object does not exist."]}}}
|
||||
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/subevents/{}/'.format(organizer.slug, event.slug, subevent.pk),
|
||||
|
||||
@@ -1061,7 +1061,7 @@ def test_create_multiple_vouchers_one_invalid(token_client, organizer, event, it
|
||||
], format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == [{}, {'code': ['Ensure this field has at least 5 characters.']}]
|
||||
assert resp.data == {1: {'code': ['Ensure this field has at least 5 characters.']}}
|
||||
with scopes_disabled():
|
||||
assert Voucher.objects.count() == 0
|
||||
|
||||
@@ -1106,7 +1106,7 @@ def test_create_multiple_vouchers_duplicate_code(token_client, organizer, event,
|
||||
], format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == [{}, {'code': ['Duplicate voucher code in request.']}]
|
||||
assert resp.data == {1: {'code': ['Duplicate voucher code in request.']}}
|
||||
with scopes_disabled():
|
||||
assert Voucher.objects.count() == 0
|
||||
|
||||
@@ -1214,7 +1214,7 @@ def test_create_multiple_vouchers_duplicate_seat(token_client, organizer, event,
|
||||
], format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == [{}, {'code': ['Duplicate seat ID in request.']}]
|
||||
assert resp.data == {1: {'code': ['Duplicate seat ID in request.']}}
|
||||
with scopes_disabled():
|
||||
assert Voucher.objects.count() == 0
|
||||
|
||||
|
||||
@@ -311,7 +311,7 @@ def test_renderer_batch_invalid(env, token_client, position):
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"parts": [{"orderposition": ["Invalid pk \"-2\" - object does not exist."]}]}
|
||||
assert resp.data == {"parts": {0: {"orderposition": ["Invalid pk \"-2\" - object does not exist."]}}}
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/ticketpdfrenderer/render_batch/'.format(env[0].slug, env[0].slug),
|
||||
@@ -326,7 +326,7 @@ def test_renderer_batch_invalid(env, token_client, position):
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"parts": [{"override_layout": ["Invalid pk \"-2\" - object does not exist."]}]}
|
||||
assert resp.data == {"parts": {0: {"override_layout": ["Invalid pk \"-2\" - object does not exist."]}}}
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/ticketpdfrenderer/render_batch/'.format(env[0].slug, env[0].slug),
|
||||
@@ -341,7 +341,7 @@ def test_renderer_batch_invalid(env, token_client, position):
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"parts": [{"override_channel": ["Object with identifier=magic does not exist."]}]}
|
||||
assert resp.data == {"parts": {0: {"override_channel": ["Object with identifier=magic does not exist."]}}}
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/ticketpdfrenderer/render_batch/'.format(env[0].slug, env[0].slug),
|
||||
@@ -356,3 +356,22 @@ def test_renderer_batch_invalid(env, token_client, position):
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"parts": ["Please do not submit more than 1000 parts."]}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_api_permission(env, token_client):
|
||||
t = env[0].organizer.teams.get()
|
||||
t.limit_event_permissions = {"event.orders:read": True}
|
||||
t.all_event_permissions = False
|
||||
t.save()
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/ticketlayouts/{}/'.format(env[0].slug, env[0].slug, env[1].pk),
|
||||
{
|
||||
"name": "Bar",
|
||||
"layout": [
|
||||
{"type": "barcodearea", "left": "7.00", "bottom": "11.15", "size": "45.00", "content": "secret"}
|
||||
]
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 403
|
||||
@@ -422,37 +422,42 @@ class OrdersTest(BaseOrdersTest):
|
||||
with scopes_disabled():
|
||||
assert self.order.invoices.count() == 1
|
||||
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
||||
'zipcode': '12345',
|
||||
'street': 'Main Street',
|
||||
'city': 'Heidelberg',
|
||||
'country': 'DE',
|
||||
'transmission_type': 'email',
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
with self.captureOnCommitCallbacks(execute=True):
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
||||
'zipcode': '12345',
|
||||
'street': 'Main Street',
|
||||
'city': 'Heidelberg',
|
||||
'country': 'DE',
|
||||
'transmission_type': 'email',
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
with scopes_disabled():
|
||||
assert self.order.invoices.count() == 3
|
||||
last_two = self.order.invoices.order_by('-pk')[:2]
|
||||
for inv in last_two:
|
||||
assert inv.transmission_status != Invoice.TRANSMISSION_STATUS_PENDING
|
||||
|
||||
self.event.settings.set('invoice_reissue_after_modify', False)
|
||||
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
||||
'zipcode': '54321',
|
||||
'street': 'Main Street',
|
||||
'city': 'Heidelberg',
|
||||
'country': 'DE',
|
||||
'transmission_type': 'email',
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
with self.captureOnCommitCallbacks(execute=True):
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/modify' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
'%s-question_%s' % (self.ticket_pos.id, self.question.id): 'ABC',
|
||||
'zipcode': '54321',
|
||||
'street': 'Main Street',
|
||||
'city': 'Heidelberg',
|
||||
'country': 'DE',
|
||||
'transmission_type': 'email',
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
with scopes_disabled():
|
||||
assert self.order.invoices.count() == 3
|
||||
|
||||
|
||||
Reference in new issue
Block a user