From e1756a1ebb1f5617e4eab3a9b0d9349db2218e41 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Mon, 28 Jul 2025 18:53:15 +0200 Subject: [PATCH] API/Vouchers: Expose "budget" and "budget_used" (Z#286557) (#5325) Co-authored-by: Richard Schreiber --- doc/api/resources/vouchers.rst | 18 ++++++++++++++---- src/pretix/api/serializers/voucher.py | 7 +++++-- src/tests/api/test_events.py | 4 ++-- src/tests/api/test_vouchers.py | 2 ++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/doc/api/resources/vouchers.rst b/doc/api/resources/vouchers.rst index 35dc782417..ff13f48c01 100644 --- a/doc/api/resources/vouchers.rst +++ b/doc/api/resources/vouchers.rst @@ -49,6 +49,8 @@ subevent integer ID of the date show_hidden_items boolean Only if set to ``true``, this voucher allows to buy products with the property ``hide_without_voucher``. Defaults to ``true``. all_addons_included boolean If set to ``true``, all add-on products for the product purchased with this voucher are included in the base price. all_bundles_included boolean If set to ``true``, all bundled products for the product purchased with this voucher are added without their designated price. +budget money (string) The budget a voucher is allowed to consume before being used up (or ``null``) +budget_used money (string) The amount of budget the voucher has already used up. ===================================== ========================== ======================================================= @@ -99,7 +101,9 @@ Endpoints "subevent": null, "show_hidden_items": false, "all_addons_included": false, - "all_bundles_included": false + "all_bundles_included": false, + "budget": None, + "budget_used": "0.00" } ] } @@ -169,7 +173,9 @@ Endpoints "subevent": null, "show_hidden_items": false, "all_addons_included": false, - "all_bundles_included": false + "all_bundles_included": false, + "budget": None, + "budget_used": "0.00" } :param organizer: The ``slug`` field of the organizer to fetch @@ -239,7 +245,9 @@ Endpoints "subevent": null, "show_hidden_items": false, "all_addons_included": false, - "all_bundles_included": false + "all_bundles_included": false, + "budget": None, + "budget_used": "0.00" } :param organizer: The ``slug`` field of the organizer to create a voucher for @@ -376,7 +384,9 @@ Endpoints "subevent": null, "show_hidden_items": false, "all_addons_included": false, - "all_bundles_included": false + "all_bundles_included": false, + "budget": None, + "budget_used": "0.00" } :param organizer: The ``slug`` field of the organizer to modify diff --git a/src/pretix/api/serializers/voucher.py b/src/pretix/api/serializers/voucher.py index 9c06db0ae2..e99c4d46b4 100644 --- a/src/pretix/api/serializers/voucher.py +++ b/src/pretix/api/serializers/voucher.py @@ -19,6 +19,8 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # +from decimal import Decimal + from rest_framework import serializers from rest_framework.exceptions import ValidationError @@ -64,14 +66,15 @@ class SeatGuidField(serializers.CharField): class VoucherSerializer(I18nAwareModelSerializer): seat = SeatGuidField(allow_null=True, required=False) + budget_used = serializers.DecimalField(read_only=True, max_digits=13, decimal_places=2, min_value=Decimal('0.00')) class Meta: model = Voucher fields = ('id', 'code', 'max_usages', 'redeemed', 'min_usages', 'valid_until', 'block_quota', 'allow_ignore_quota', 'price_mode', 'value', 'item', 'variation', 'quota', 'tag', 'comment', 'subevent', 'show_hidden_items', 'seat', 'all_addons_included', - 'all_bundles_included') - read_only_fields = ('id', 'redeemed') + 'all_bundles_included', 'budget', 'budget_used') + read_only_fields = ('id', 'redeemed', 'budget_used') list_serializer_class = VoucherListSerializer def validate(self, data): diff --git a/src/tests/api/test_events.py b/src/tests/api/test_events.py index bb3a952a6d..6737af5fe0 100644 --- a/src/tests/api/test_events.py +++ b/src/tests/api/test_events.py @@ -1750,7 +1750,7 @@ def test_event_expand_seat_filter_and_querycount(token_client, organizer, event, with scope(organizer=organizer): v0 = event.vouchers.create(item=item, seat=event.seats.get(seat_guid='0-0')) - with assert_num_queries(13): + with assert_num_queries(14): resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/' '?expand=orderposition&expand=cartposition&expand=voucher&is_available=false' .format(organizer.slug, event.slug)) @@ -1769,7 +1769,7 @@ def test_event_expand_seat_filter_and_querycount(token_client, organizer, event, v1 = event.vouchers.create(item=item, seat=event.seats.get(seat_guid='0-1')) v2 = event.vouchers.create(item=item, seat=event.seats.get(seat_guid='0-2')) - with assert_num_queries(13): + with assert_num_queries(16): resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/' '?expand=orderposition&expand=cartposition&expand=voucher&is_available=false' .format(organizer.slug, event.slug)) diff --git a/src/tests/api/test_vouchers.py b/src/tests/api/test_vouchers.py index 15d37b7c24..227a419fd4 100644 --- a/src/tests/api/test_vouchers.py +++ b/src/tests/api/test_vouchers.py @@ -81,6 +81,8 @@ TEST_VOUCHER_RES = { 'all_bundles_included': False, 'subevent': None, 'seat': None, + 'budget': None, + 'budget_used': "0.00", }