forked from CGM_Public/pretix_original
Allow plugins to add data to the order API (Z#23179688) (#4822)
* Allow plugins to add data to the order API (Z#23179688) * Update src/pretix/api/serializers/media.py Co-authored-by: Richard Schreiber <schreiber@rami.io> * Fix failing test --------- Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
@@ -46,6 +46,7 @@ from pretix.api.serializers.i18n import I18nAwareModelSerializer
|
||||
from pretix.api.serializers.item import (
|
||||
InlineItemVariationSerializer, ItemSerializer, QuestionSerializer,
|
||||
)
|
||||
from pretix.api.signals import order_api_details, orderposition_api_details
|
||||
from pretix.base.decimal import round_decimal
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
@@ -494,6 +495,18 @@ class OrderPositionListSerializer(serializers.ListSerializer):
|
||||
return data
|
||||
|
||||
|
||||
class OrderPositionPluginDataField(serializers.Field):
|
||||
def to_representation(self, value: OrderPosition):
|
||||
d = {}
|
||||
if value and value.pk:
|
||||
for recv, resp in orderposition_api_details.send(
|
||||
sender=self.context.get("event") or value.order.event,
|
||||
orderposition=value
|
||||
):
|
||||
d.update(resp)
|
||||
return d
|
||||
|
||||
|
||||
class OrderPositionSerializer(I18nAwareModelSerializer):
|
||||
checkins = CheckinSerializer(many=True, read_only=True)
|
||||
print_logs = PrintLogSerializer(many=True, read_only=True)
|
||||
@@ -504,6 +517,7 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
|
||||
seat = InlineSeatSerializer(read_only=True)
|
||||
country = CompatibleCountryField(source='*')
|
||||
attendee_name = serializers.CharField(required=False)
|
||||
plugin_data = OrderPositionPluginDataField(source='*', allow_null=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
list_serializer_class = OrderPositionListSerializer
|
||||
@@ -513,7 +527,7 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
|
||||
'attendee_email', 'voucher', 'tax_rate', 'tax_value', 'secret', 'addon_to', 'subevent', 'checkins',
|
||||
'print_logs', 'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'canceled',
|
||||
'print_logs', 'downloads', 'answers', 'tax_rule', 'tax_code', 'pseudonymization_id', 'pdf_data', 'seat',
|
||||
'canceled', 'valid_from', 'valid_until', 'blocked', 'voucher_budget_use')
|
||||
'canceled', 'valid_from', 'valid_until', 'blocked', 'voucher_budget_use', 'plugin_data')
|
||||
read_only_fields = (
|
||||
'id', 'order', 'positionid', 'item', 'variation', 'price', 'voucher', 'tax_rate', 'tax_value', 'secret',
|
||||
'addon_to', 'subevent', 'checkins', 'downloads', 'answers', 'tax_rule', 'tax_code', 'pseudonymization_id',
|
||||
@@ -730,6 +744,18 @@ class OrderListSerializer(serializers.ListSerializer):
|
||||
return data
|
||||
|
||||
|
||||
class OrderPluginDataField(serializers.Field):
|
||||
def to_representation(self, value: Order):
|
||||
d = {}
|
||||
if value and value.pk:
|
||||
for recv, resp in order_api_details.send(
|
||||
sender=self.context.get("event") or value.event,
|
||||
order=value
|
||||
):
|
||||
d.update(resp)
|
||||
return d
|
||||
|
||||
|
||||
class OrderSerializer(I18nAwareModelSerializer):
|
||||
event = SlugRelatedField(slug_field='slug', read_only=True)
|
||||
invoice_address = InvoiceAddressSerializer(allow_null=True)
|
||||
@@ -747,6 +773,7 @@ class OrderSerializer(I18nAwareModelSerializer):
|
||||
queryset=SalesChannel.objects.none(),
|
||||
required=False,
|
||||
)
|
||||
plugin_data = OrderPluginDataField(source='*', allow_null=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Order
|
||||
@@ -755,7 +782,7 @@ class OrderSerializer(I18nAwareModelSerializer):
|
||||
'code', 'event', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
|
||||
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
|
||||
'checkin_attention', 'checkin_text', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
|
||||
'url', 'customer', 'valid_if_pending', 'api_meta', 'cancellation_date'
|
||||
'url', 'customer', 'valid_if_pending', 'api_meta', 'cancellation_date', 'plugin_data',
|
||||
)
|
||||
read_only_fields = (
|
||||
'code', 'status', 'testmode', 'secret', 'datetime', 'expires', 'payment_date',
|
||||
|
||||
@@ -26,7 +26,7 @@ from django.utils.timezone import now
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from pretix.api.models import ApiCall, WebHookCall
|
||||
from pretix.base.signals import periodic_task
|
||||
from pretix.base.signals import EventPluginSignal, periodic_task
|
||||
from pretix.helpers.periodic import minimum_interval
|
||||
|
||||
register_webhook_events = Signal()
|
||||
@@ -43,6 +43,28 @@ return an instance of a subclass of ``pretix.api.auth.devicesecurity.BaseSecurit
|
||||
or a list of such instances.
|
||||
"""
|
||||
|
||||
order_api_details = EventPluginSignal()
|
||||
"""
|
||||
Arguments: ``order``
|
||||
|
||||
This signal is sent out to fill the ``plugin_details`` field of the order API. Receivers
|
||||
should return a dictionary that is combined with the dictionaries of all other plugins.
|
||||
Note that doing database or network queries in receivers to this signal is discouraged
|
||||
and could cause serious performance issues. The main purpose is to provide information
|
||||
from e.g. ``meta_info`` to the API consumer,
|
||||
"""
|
||||
|
||||
orderposition_api_details = EventPluginSignal()
|
||||
"""
|
||||
Arguments: ``orderposition``
|
||||
|
||||
This signal is sent out to fill the ``plugin_details`` field of the order API. Receivers
|
||||
should return a dictionary that is combined with the dictionaries of all other plugins.
|
||||
Note that doing database or network queries in receivers to this signal is discouraged
|
||||
and could cause serious performance issues. The main purpose is to provide information
|
||||
from e.g. ``meta_info`` to the API consumer,
|
||||
"""
|
||||
|
||||
|
||||
@receiver(periodic_task)
|
||||
@scopes_disabled()
|
||||
|
||||
@@ -162,6 +162,7 @@ def test_giftcard_detail_expand(token_client, organizer, event, giftcard):
|
||||
"tax_rule": None,
|
||||
"pseudonymization_id": op.pseudonymization_id,
|
||||
"pdf_data": {},
|
||||
"plugin_data": {},
|
||||
"seat": None,
|
||||
"canceled": False,
|
||||
"valid_from": None,
|
||||
|
||||
@@ -477,7 +477,8 @@ def test_order_create_simulate(token_client, organizer, event, item, quota, ques
|
||||
'zipcode': None,
|
||||
'state': None,
|
||||
'country': None,
|
||||
'canceled': False
|
||||
'canceled': False,
|
||||
'plugin_data': {},
|
||||
}
|
||||
],
|
||||
'downloads': [],
|
||||
@@ -487,7 +488,8 @@ def test_order_create_simulate(token_client, organizer, event, item, quota, ques
|
||||
'refunds': [],
|
||||
'require_approval': False,
|
||||
'sales_channel': 'web',
|
||||
'cancellation_date': None
|
||||
'cancellation_date': None,
|
||||
'plugin_data': {},
|
||||
}
|
||||
|
||||
|
||||
@@ -533,13 +535,15 @@ def test_order_create_positionids_addons_simulated(token_client, organizer, even
|
||||
'street': None, 'zipcode': None, 'city': None, 'country': None, 'state': None, 'attendee_email': None,
|
||||
'voucher': None, 'tax_rate': '19.00', 'tax_code': 'S/standard', 'tax_value': '3.67', 'discount': None, 'voucher_budget_use': None,
|
||||
'addon_to': None, 'subevent': None, 'checkins': [], 'print_logs': [], 'downloads': [], 'answers': [], 'tax_rule': item.tax_rule_id,
|
||||
'pseudonymization_id': 'PREVIEW', 'seat': None, 'canceled': False, 'valid_from': None, 'valid_until': None, 'blocked': None},
|
||||
'pseudonymization_id': 'PREVIEW', 'seat': None, 'canceled': False, 'valid_from': None, 'valid_until': None, 'blocked': None,
|
||||
'plugin_data': {}},
|
||||
{'id': 0, 'order': '', 'positionid': 2, 'item': item.pk, 'variation': None, 'price': '23.00',
|
||||
'attendee_name': 'Peter', 'attendee_name_parts': {'full_name': 'Peter', '_scheme': 'full'}, 'company': None,
|
||||
'street': None, 'zipcode': None, 'city': None, 'country': None, 'state': None, 'attendee_email': None,
|
||||
'voucher': None, 'tax_rate': '19.00', 'tax_code': 'S/standard', 'tax_value': '3.67', 'discount': None, 'voucher_budget_use': None,
|
||||
'addon_to': 1, 'subevent': None, 'checkins': [], 'print_logs': [], 'downloads': [], 'answers': [], 'tax_rule': item.tax_rule_id,
|
||||
'pseudonymization_id': 'PREVIEW', 'seat': None, 'canceled': False, 'valid_from': None, 'valid_until': None, 'blocked': None}
|
||||
'pseudonymization_id': 'PREVIEW', 'seat': None, 'canceled': False, 'valid_from': None, 'valid_until': None, 'blocked': None,
|
||||
'plugin_data': {}}
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -236,6 +236,7 @@ TEST_ORDERPOSITION_RES = {
|
||||
],
|
||||
"subevent": None,
|
||||
"canceled": False,
|
||||
"plugin_data": {},
|
||||
}
|
||||
TEST_PAYMENTS_RES = [
|
||||
{
|
||||
@@ -333,6 +334,7 @@ TEST_ORDER_RES = {
|
||||
"payments": TEST_PAYMENTS_RES,
|
||||
"refunds": TEST_REFUNDS_RES,
|
||||
"cancellation_date": None,
|
||||
"plugin_data": {},
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -203,7 +203,8 @@ def test_medium_detail(token_client, organizer, event, medium, giftcard, custome
|
||||
"canceled": False,
|
||||
"valid_from": None,
|
||||
"valid_until": None,
|
||||
"blocked": None
|
||||
"blocked": None,
|
||||
"plugin_data": {},
|
||||
}
|
||||
assert resp.data["linked_giftcard"] == {
|
||||
"id": giftcard.pk,
|
||||
|
||||
Reference in New Issue
Block a user