mirror of
https://github.com/pretix/pretix.git
synced 2026-08-18 12:06:26 +00:00
API: Allow to return canceled positions and fees
This commit is contained in:
@@ -61,9 +61,10 @@ invoice_address object Invoice address
|
||||
└ vat_id_validated string ``true``, if the VAT ID has been validated against the
|
||||
EU VAT service and validation was successful. This only
|
||||
happens in rare cases.
|
||||
positions list of objects List of non-canceled order positions (see below)
|
||||
fees list of objects List of non-canceled fees included in the order total
|
||||
(i.e. payment fees)
|
||||
positions list of objects List of order positions (see below). By default, only
|
||||
non-canceled positions are included.
|
||||
fees list of objects List of fees included in the order total. By default, only
|
||||
non-canceled fees are included.
|
||||
├ fee_type string Type of fee (currently ``payment``, ``passbook``,
|
||||
``other``)
|
||||
├ value money (string) Fee amount
|
||||
@@ -72,7 +73,8 @@ fees list of objects List of non-can
|
||||
can be empty
|
||||
├ tax_rate decimal (string) VAT rate applied for this fee
|
||||
├ tax_value money (string) VAT included in this fee
|
||||
└ tax_rule integer The ID of the used tax rule (or ``null``)
|
||||
├ tax_rule integer The ID of the used tax rule (or ``null``)
|
||||
└ canceled boolean Whether or not this fee has been canceled.
|
||||
downloads list of objects List of ticket download options for order-wise ticket
|
||||
downloading. This might be a multi-page PDF or a ZIP
|
||||
file of tickets for outputs that do not support
|
||||
@@ -145,6 +147,10 @@ last_modified datetime Last modificati
|
||||
The ``invoice_address.state`` and ``url`` attributes have been added. When creating orders through the API,
|
||||
vouchers are now supported and many fields are now optional.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
|
||||
The ``order.fees.canceled`` attribute has been added.
|
||||
|
||||
|
||||
.. _order-position-resource:
|
||||
|
||||
@@ -159,6 +165,8 @@ Field Type Description
|
||||
id integer Internal ID of the order position
|
||||
order string Order code of the order the position belongs to
|
||||
positionid integer Number of the position within the order
|
||||
canceled boolean Whether or not this position has been canceled. Note that
|
||||
by default, only non-canceled positions are shown.
|
||||
item integer ID of the purchased item
|
||||
variation integer ID of the purchased variation (or ``null``)
|
||||
price money (string) Price of this position
|
||||
@@ -224,6 +232,10 @@ pdf_data object Data object req
|
||||
The ``url`` of a ticket ``download`` can now also return a ``text/uri-list`` instead of a file. See
|
||||
:ref:`order-position-ticket-download` for details.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
|
||||
The attribute ``canceled`` has been added.
|
||||
|
||||
.. _order-payment-resource:
|
||||
|
||||
Order payment resource
|
||||
@@ -290,6 +302,10 @@ List of all orders
|
||||
|
||||
Filtering for emails or order codes is now case-insensitive.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
|
||||
The ``include_canceled_positions`` and ``include_canceled_fees`` query parameters have been added.
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/orders/
|
||||
|
||||
Returns a list of all orders within a given event.
|
||||
@@ -355,6 +371,7 @@ List of all orders
|
||||
"id": 23442,
|
||||
"order": "ABC12",
|
||||
"positionid": 1,
|
||||
"canceled": false,
|
||||
"item": 1345,
|
||||
"variation": null,
|
||||
"price": "23.00",
|
||||
@@ -427,6 +444,9 @@ List of all orders
|
||||
:query boolean testmode: Only return orders with ``testmode`` set to ``true`` or ``false``
|
||||
:query boolean require_approval: If set to ``true`` or ``false``, only categories with this value for the field
|
||||
``require_approval`` will be returned.
|
||||
:query include_canceled_positions: If set to ``true``, the output will contain canceled order positions. Note that this
|
||||
only affects position-level cancellations, not fully-canceled orders.
|
||||
:query include_canceled_fees: If set to ``true``, the output will contain cancaled order fees.
|
||||
:query string email: Only return orders created with the given email address
|
||||
:query string locale: Only return orders with the given customer locale
|
||||
:query datetime modified_since: Only return orders that have changed since the given date. Be careful: We only
|
||||
@@ -444,6 +464,10 @@ List of all orders
|
||||
Fetching individual orders
|
||||
--------------------------
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
|
||||
The ``include_canceled_positions`` and ``include_canceled_fees`` query parameters have been added.
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/orders/(code)/
|
||||
|
||||
Returns information on one order, identified by its order code.
|
||||
@@ -503,6 +527,7 @@ Fetching individual orders
|
||||
"id": 23442,
|
||||
"order": "ABC12",
|
||||
"positionid": 1,
|
||||
"canceled": false,
|
||||
"item": 1345,
|
||||
"variation": null,
|
||||
"price": "23.00",
|
||||
@@ -568,6 +593,9 @@ Fetching individual orders
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:param code: The ``code`` field of the order to fetch
|
||||
:query include_canceled_positions: If set to ``true``, the output will contain canceled order positions. Note that this
|
||||
only affects position-level cancellations, not fully-canceled orders.
|
||||
:query include_canceled_fees: If set to ``true``, the output will contain cancaled order fees.
|
||||
: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.
|
||||
@@ -1313,8 +1341,9 @@ List of all order positions
|
||||
|
||||
The value ``auto_checked_in`` has been added to the ``checkins``-attribute.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
|
||||
.. note:: Individually canceled order positions are currently not visible via the API at all.
|
||||
The ``include_canceled_positions`` and ``include_canceled_fees`` query parameters have been added.
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/orderpositions/
|
||||
|
||||
@@ -1345,6 +1374,7 @@ List of all order positions
|
||||
"id": 23442,
|
||||
"order": "ABC12",
|
||||
"positionid": 1,
|
||||
"canceled": false,
|
||||
"item": 1345,
|
||||
"variation": null,
|
||||
"price": "23.00",
|
||||
@@ -1414,6 +1444,8 @@ List of all order positions
|
||||
comma-separated IDs.
|
||||
:query string voucher: Only return positions with a specific voucher.
|
||||
:query string voucher__code: Only return positions with a specific voucher code.
|
||||
:query include_canceled_positions: If set to ``true``, the output will contain canceled order positions. Note that this
|
||||
only affects position-level cancellations, not fully-canceled orders.
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:statuscode 200: no error
|
||||
@@ -1447,6 +1479,7 @@ Fetching individual positions
|
||||
"id": 23442,
|
||||
"order": "ABC12",
|
||||
"positionid": 1,
|
||||
"canceled": false,
|
||||
"item": 1345,
|
||||
"variation": null,
|
||||
"price": "23.00",
|
||||
@@ -1491,6 +1524,7 @@ Fetching individual positions
|
||||
: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 order position to fetch
|
||||
:query include_canceled_positions: If set to ``true``, canceled positions may be returned (otherwise, they return 404).
|
||||
: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.
|
||||
|
||||
@@ -204,7 +204,7 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
|
||||
model = OrderPosition
|
||||
fields = ('id', 'order', 'positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts',
|
||||
'attendee_email', 'voucher', 'tax_rate', 'tax_value', 'secret', 'addon_to', 'subevent', 'checkins',
|
||||
'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat')
|
||||
'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'canceled')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -284,7 +284,7 @@ class OrderPaymentDateField(serializers.DateField):
|
||||
class OrderFeeSerializer(I18nAwareModelSerializer):
|
||||
class Meta:
|
||||
model = OrderFee
|
||||
fields = ('fee_type', 'value', 'description', 'internal_type', 'tax_rate', 'tax_value', 'tax_rule')
|
||||
fields = ('fee_type', 'value', 'description', 'internal_type', 'tax_rate', 'tax_value', 'tax_rule', 'canceled')
|
||||
|
||||
|
||||
class PaymentURLField(serializers.URLField):
|
||||
|
||||
@@ -30,8 +30,8 @@ from pretix.api.serializers.order import (
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
CachedCombinedTicket, CachedTicket, Device, Event, Invoice, InvoiceAddress,
|
||||
Order, OrderPayment, OrderPosition, OrderRefund, Quota, TeamAPIToken,
|
||||
generate_position_secret, generate_secret,
|
||||
Order, OrderFee, OrderPayment, OrderPosition, OrderRefund, Quota,
|
||||
TeamAPIToken, generate_position_secret, generate_secret,
|
||||
)
|
||||
from pretix.base.payment import PaymentException
|
||||
from pretix.base.services import tickets
|
||||
@@ -82,20 +82,29 @@ class OrderViewSet(viewsets.ModelViewSet):
|
||||
return ctx
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.query_params.get('include_canceled_fees', 'false') == 'true':
|
||||
fqs = OrderFee.all
|
||||
else:
|
||||
fqs = OrderFee.objects
|
||||
qs = self.request.event.orders.prefetch_related(
|
||||
'fees', 'payments', 'refunds', 'refunds__payment'
|
||||
Prefetch('fees', queryset=fqs.all()),
|
||||
'payments', 'refunds', 'refunds__payment'
|
||||
).select_related(
|
||||
'invoice_address'
|
||||
)
|
||||
|
||||
if self.request.query_params.get('include_canceled_positions', 'false') == 'true':
|
||||
opq = OrderPosition.all
|
||||
else:
|
||||
opq = OrderPosition.objects
|
||||
if self.request.query_params.get('pdf_data', 'false') == 'true':
|
||||
qs = qs.prefetch_related(
|
||||
Prefetch(
|
||||
'positions',
|
||||
OrderPosition.objects.all().prefetch_related(
|
||||
opq.all().prefetch_related(
|
||||
'checkins', 'item', 'variation', 'answers', 'answers__options', 'answers__question',
|
||||
'item__category', 'addon_to', 'seat',
|
||||
Prefetch('addons', OrderPosition.objects.select_related('item', 'variation', 'seat'))
|
||||
Prefetch('addons', opq.select_related('item', 'variation', 'seat'))
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -103,7 +112,7 @@ class OrderViewSet(viewsets.ModelViewSet):
|
||||
qs = qs.prefetch_related(
|
||||
Prefetch(
|
||||
'positions',
|
||||
OrderPosition.objects.all().prefetch_related(
|
||||
opq.all().prefetch_related(
|
||||
'checkins', 'item', 'variation', 'answers', 'answers__options', 'answers__question', 'seat',
|
||||
)
|
||||
)
|
||||
@@ -654,11 +663,16 @@ class OrderPositionViewSet(mixins.DestroyModelMixin, viewsets.ReadOnlyModelViewS
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
qs = OrderPosition.objects.filter(order__event=self.request.event)
|
||||
if self.request.query_params.get('include_canceled_positions', 'false') == 'true':
|
||||
qs = OrderPosition.all
|
||||
else:
|
||||
qs = OrderPosition.objects
|
||||
|
||||
qs = qs.filter(order__event=self.request.event)
|
||||
if self.request.query_params.get('pdf_data', 'false') == 'true':
|
||||
qs = qs.prefetch_related(
|
||||
'checkins', 'answers', 'answers__options', 'answers__question',
|
||||
Prefetch('addons', OrderPosition.objects.select_related('item', 'variation')),
|
||||
Prefetch('addons', qs.select_related('item', 'variation')),
|
||||
Prefetch('order', Order.objects.select_related('invoice_address').prefetch_related(
|
||||
Prefetch(
|
||||
'event',
|
||||
@@ -666,7 +680,7 @@ class OrderPositionViewSet(mixins.DestroyModelMixin, viewsets.ReadOnlyModelViewS
|
||||
),
|
||||
Prefetch(
|
||||
'positions',
|
||||
OrderPosition.objects.prefetch_related(
|
||||
qs.prefetch_related(
|
||||
'checkins', 'item', 'variation', 'answers', 'answers__options', 'answers__question',
|
||||
)
|
||||
)
|
||||
@@ -676,7 +690,7 @@ class OrderPositionViewSet(mixins.DestroyModelMixin, viewsets.ReadOnlyModelViewS
|
||||
)
|
||||
else:
|
||||
qs = qs.prefetch_related(
|
||||
'checkins', 'answers', 'answers__options', 'answers__question'
|
||||
'checkins', 'answers', 'answers__options', 'answers__question',
|
||||
).select_related(
|
||||
'item', 'order', 'order__event', 'order__event__organizer', 'seat'
|
||||
)
|
||||
|
||||
@@ -114,6 +114,8 @@ def order(event, item, taxrule, question):
|
||||
)
|
||||
o.fees.create(fee_type=OrderFee.FEE_TYPE_PAYMENT, value=Decimal('0.25'), tax_rate=Decimal('19.00'),
|
||||
tax_value=Decimal('0.05'), tax_rule=taxrule)
|
||||
o.fees.create(fee_type=OrderFee.FEE_TYPE_PAYMENT, value=Decimal('0.25'), tax_rate=Decimal('19.00'),
|
||||
tax_value=Decimal('0.05'), tax_rule=taxrule, canceled=True)
|
||||
InvoiceAddress.objects.create(order=o, company="Sample company", country=Country('NZ'),
|
||||
vat_id="DE123", vat_id_validated=True)
|
||||
op = OrderPosition.objects.create(
|
||||
@@ -174,7 +176,8 @@ TEST_ORDERPOSITION_RES = {
|
||||
"option_identifiers": []
|
||||
}
|
||||
],
|
||||
"subevent": None
|
||||
"subevent": None,
|
||||
"canceled": False,
|
||||
}
|
||||
TEST_PAYMENTS_RES = [
|
||||
{
|
||||
@@ -226,6 +229,7 @@ TEST_ORDER_RES = {
|
||||
"sales_channel": "web",
|
||||
"fees": [
|
||||
{
|
||||
"canceled": False,
|
||||
"fee_type": "payment",
|
||||
"value": "0.25",
|
||||
"description": "",
|
||||
@@ -318,6 +322,22 @@ def test_order_list(token_client, organizer, event, order, item, taxrule, questi
|
||||
))
|
||||
assert [] == resp.data['results']
|
||||
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?include_canceled_positions=false'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.data['results'][0]['positions']) == 1
|
||||
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?include_canceled_positions=true'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.data['results'][0]['positions']) == 2
|
||||
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?include_canceled_fees=false'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.data['results'][0]['fees']) == 1
|
||||
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?include_canceled_fees=true'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.data['results'][0]['fees']) == 2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_detail(token_client, organizer, event, order, item, taxrule, question):
|
||||
@@ -354,6 +374,16 @@ def test_order_detail(token_client, organizer, event, order, item, taxrule, ques
|
||||
assert len(resp.data['downloads']) == 1
|
||||
assert len(resp.data['positions'][0]['downloads']) == 1
|
||||
|
||||
assert len(resp.data['positions']) == 1
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/?include_canceled_positions=true'.format(organizer.slug, event.slug, order.code))
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.data['positions']) == 2
|
||||
|
||||
assert len(resp.data['fees']) == 1
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/?include_canceled_fees=true'.format(organizer.slug, event.slug, order.code))
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.data['fees']) == 2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_payment_list(token_client, organizer, event, order):
|
||||
@@ -743,6 +773,13 @@ def test_orderposition_list(token_client, organizer, event, order, item, subeven
|
||||
subevent.pk + 1))
|
||||
assert [] == resp.data['results']
|
||||
|
||||
resp = token_client.get(
|
||||
'/api/v1/organizers/{}/events/{}/orderpositions/?include_canceled_positions=false'.format(organizer.slug, event.slug))
|
||||
assert len(resp.data['results']) == 1
|
||||
resp = token_client.get(
|
||||
'/api/v1/organizers/{}/events/{}/orderpositions/?include_canceled_positions=true'.format(organizer.slug, event.slug))
|
||||
assert len(resp.data['results']) == 2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_orderposition_detail(token_client, organizer, event, order, item, question):
|
||||
@@ -766,12 +803,15 @@ def test_orderposition_detail(token_client, organizer, event, order, item, quest
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_orderposition_detail_no_canceled(token_client, organizer, event, order, item, question):
|
||||
def test_orderposition_detail_canceled(token_client, organizer, event, order, item, question):
|
||||
with scopes_disabled():
|
||||
op = order.all_positions.filter(canceled=True).first()
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orderpositions/{}/'.format(organizer.slug, event.slug,
|
||||
op.pk))
|
||||
assert resp.status_code == 404
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orderpositions/{}/?include_canceled_positions=true'.format(
|
||||
organizer.slug, event.slug, op.pk))
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
||||
Reference in New Issue
Block a user