From 2ad9e1bb430ad4fbed16b9392f14d1b5d0e2b9eb Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Tue, 18 Jul 2017 10:37:32 +0200 Subject: [PATCH] Change API to accomodate for invoice numbers --- doc/api/resources/invoices.rst | 19 +++++++++++++------ src/pretix/api/serializers/order.py | 2 +- src/pretix/api/views/order.py | 26 +++++++++++++++++++------- src/tests/api/test_orders.py | 18 +++++++++--------- 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/doc/api/resources/invoices.rst b/doc/api/resources/invoices.rst index 7d6b83dcaa..0c88257a31 100644 --- a/doc/api/resources/invoices.rst +++ b/doc/api/resources/invoices.rst @@ -11,7 +11,7 @@ The invoice resource contains the following public fields: ===================================== ========================== ======================================================= Field Type Description ===================================== ========================== ======================================================= -invoice_no string Invoice number (without prefix) +number string Invoice number (with prefix) order string Order code of the order this invoice belongs to is_cancellation boolean ``True``, if this invoice is the cancellation of a different invoice. @@ -35,6 +35,13 @@ lines list of objects The actual invo ===================================== ========================== ======================================================= +.. versionchanged:: 1.6 + + The attribute ``invoice_no`` has been dropped in favor of ``number`` which includes the number including the prefix, + since the prefix can now vary. Also, invoices now need to be identified by their ``number`` instead of the raw + number. + + Endpoints --------- @@ -64,7 +71,7 @@ Endpoints "previous": null, "results": [ { - "invoice_no": "00001", + "number": "SAMPLECONF-00001", "order": "ABC12", "is_cancellation": false, "invoice_from": "Big Events LLC\nDemo street 12\nDemo town", @@ -95,14 +102,14 @@ Endpoints :query string refers: If set, only invoices refering to the given invoice will be returned. :query string locale: If set, only invoices with the given locale will be returned. :query string ordering: Manually set the ordering of results. Valid fields to be used are ``date`` and - ``invoice_no``. Default: ``invoice_no`` + ``nr`` (equals to ``number``). Default: ``nr`` :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)/invoices/(invoice_no)/ +.. http:get:: /api/v1/organizers/(organizer)/events/(event)/invoices/(number)/ Returns information on one invoice, identified by its invoice number. @@ -110,7 +117,7 @@ Endpoints .. sourcecode:: http - GET /api/v1/organizers/bigevents/events/sampleconf/invoices/00001/ HTTP/1.1 + GET /api/v1/organizers/bigevents/events/sampleconf/invoices/SAMPLECONF-00001/ HTTP/1.1 Host: pretix.eu Accept: application/json, text/javascript @@ -123,7 +130,7 @@ Endpoints Content-Type: text/javascript { - "invoice_no": "00001", + "number": "SAMPLECONF-00001", "order": "ABC12", "is_cancellation": false, "invoice_from": "Big Events LLC\nDemo street 12\nDemo town", diff --git a/src/pretix/api/serializers/order.py b/src/pretix/api/serializers/order.py index 11d332a9d9..79205b4c19 100644 --- a/src/pretix/api/serializers/order.py +++ b/src/pretix/api/serializers/order.py @@ -115,5 +115,5 @@ class InvoiceSerializer(I18nAwareModelSerializer): class Meta: model = Invoice - fields = ('order', 'invoice_no', 'is_cancellation', 'invoice_from', 'invoice_to', 'date', 'refers', 'locale', + fields = ('order', 'number', 'is_cancellation', 'invoice_from', 'invoice_to', 'date', 'refers', 'locale', 'introductory_text', 'additional_text', 'payment_provider_text', 'footer_text', 'lines') diff --git a/src/pretix/api/views/order.py b/src/pretix/api/views/order.py index fb30b716fb..4de69ba046 100644 --- a/src/pretix/api/views/order.py +++ b/src/pretix/api/views/order.py @@ -1,5 +1,6 @@ import django_filters from django.db.models import Q +from django.db.models.functions import Concat from django.http import FileResponse from django_filters.rest_framework import DjangoFilterBackend, FilterSet from rest_framework import viewsets @@ -137,12 +138,21 @@ class OrderPositionViewSet(viewsets.ReadOnlyModelViewSet): class InvoiceFilter(FilterSet): - refers = django_filters.CharFilter(name='refers', lookup_expr='invoice_no__iexact') + refers = django_filters.CharFilter(method='refers_qs') + number = django_filters.CharFilter(method='nr_qs') order = django_filters.CharFilter(name='order', lookup_expr='code__iexact') + def refers_qs(self, queryset, name, value): + return queryset.annotate( + refers_nr=Concat('refers__prefix', 'refers__invoice_no') + ).filter(refers_nr__iexact=value) + + def nr_qs(self, queryset, name, value): + return queryset.filter(nr__iexact=value) + class Meta: model = Invoice - fields = ['order', 'invoice_no', 'is_cancellation', 'refers', 'locale'] + fields = ['order', 'number', 'is_cancellation', 'refers', 'locale'] class RetryException(APIException): @@ -155,15 +165,17 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = InvoiceSerializer queryset = Invoice.objects.none() filter_backends = (DjangoFilterBackend, OrderingFilter) - ordering = ('invoice_no',) - ordering_fields = ('invoice_no', 'date') + ordering = ('nr',) + ordering_fields = ('nr', 'date') filter_class = InvoiceFilter - lookup_field = 'invoice_no' - lookup_url_kwarg = 'invoice_no' permission = 'can_view_orders' + lookup_url_kwarg = 'number' + lookup_field = 'nr' def get_queryset(self): - return self.request.event.invoices.prefetch_related('lines').select_related('order') + return self.request.event.invoices.prefetch_related('lines').select_related('order', 'refers').annotate( + nr=Concat('prefix', 'invoice_no') + ) @detail_route() def download(self, request, **kwargs): diff --git a/src/tests/api/test_orders.py b/src/tests/api/test_orders.py index 45bfca0264..8729d1f437 100644 --- a/src/tests/api/test_orders.py +++ b/src/tests/api/test_orders.py @@ -256,7 +256,7 @@ def invoice(order): TEST_INVOICE_RES = { "order": "FOO", - "invoice_no": "00001", + "number": "DUMMY-00001", "is_cancellation": False, "invoice_from": "", "invoice_to": "Sample company", @@ -291,10 +291,10 @@ def test_invoice_list(token_client, organizer, event, order, invoice): resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?order=BAR'.format(organizer.slug, event.slug)) assert [] == resp.data['results'] - resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?invoice_no={}'.format( - organizer.slug, event.slug, invoice.invoice_no)) + resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?number={}'.format( + organizer.slug, event.slug, invoice.number)) assert [res] == resp.data['results'] - resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?invoice_no=XXX'.format( + resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?number=XXX'.format( organizer.slug, event.slug)) assert [] == resp.data['results'] @@ -313,15 +313,15 @@ def test_invoice_list(token_client, organizer, event, order, invoice): resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?is_cancellation=true'.format( organizer.slug, event.slug)) assert len(resp.data['results']) == 1 - assert resp.data['results'][0]['invoice_no'] == ic.invoice_no + assert resp.data['results'][0]['number'] == ic.number resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?refers={}'.format( - organizer.slug, event.slug, invoice.invoice_no)) + organizer.slug, event.slug, invoice.number)) assert len(resp.data['results']) == 1 - assert resp.data['results'][0]['invoice_no'] == ic.invoice_no + assert resp.data['results'][0]['number'] == ic.number resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/?refers={}'.format( - organizer.slug, event.slug, ic.invoice_no)) + organizer.slug, event.slug, ic.number)) assert [] == resp.data['results'] @@ -330,6 +330,6 @@ def test_invoice_detail(token_client, organizer, event, invoice): res = dict(TEST_INVOICE_RES) resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/{}/'.format(organizer.slug, event.slug, - invoice.invoice_no)) + invoice.number)) assert resp.status_code == 200 assert res == resp.data