diff --git a/doc/api/resources/giftcards.rst b/doc/api/resources/giftcards.rst index 6756be13f..5091affc9 100644 --- a/doc/api/resources/giftcards.rst +++ b/doc/api/resources/giftcards.rst @@ -24,6 +24,8 @@ owner_ticket integer Internal ID of this gift card and can view all transactions. When setting this field, you can also give the ``secret`` of an order position. +issuer string Organizer slug of the organizer who created this gift + card and is responsible for it. ===================================== ========================== ======================================================= The gift card transaction resource contains the following public fields: @@ -39,8 +41,17 @@ value money (string) Transaction amo event string Event slug, if the gift card was used in the web shop (or ``null``) order string Order code, if the gift card was used in the web shop (or ``null``) text string Custom text of the transaction (or ``null``) +info object Additional data about the transaction (or ``null``) +acceptor string Organizer slug of the organizer who created this transaction + (can be ``null`` for all transactions performed before + this field was added.) ===================================== ========================== ======================================================= +.. versionchanged:: 4.20 + + The ``owner_ticket`` and ``issuer`` attributes of the gift card and the ``info`` and ``acceptor`` attributes of the + gift card transaction resource have been added. + Endpoints --------- @@ -77,6 +88,7 @@ Endpoints "expires": null, "conditions": null, "owner_ticket": null, + "issuer": "bigevents", "value": "13.37" } ] @@ -123,6 +135,7 @@ Endpoints "expires": null, "conditions": null, "owner_ticket": null, + "issuer": "bigevents", "value": "13.37" } @@ -168,6 +181,7 @@ Endpoints "expires": null, "conditions": null, "owner_ticket": null, + "issuer": "bigevents", "value": "13.37" } @@ -221,6 +235,7 @@ Endpoints "expires": null, "conditions": null, "owner_ticket": null, + "issuer": "bigevents", "value": "14.00" } @@ -267,6 +282,7 @@ Endpoints "expires": null, "conditions": null, "owner_ticket": null, + "issuer": "bigevents", "value": "15.37" } @@ -310,7 +326,11 @@ Endpoints "value": "50.00", "event": "democon", "order": "FXQYW", - "text": null + "text": null, + "acceptor": "bigevents", + "info": { + "created_by": "plugin1" + } } ] } diff --git a/doc/development/api/general.rst b/doc/development/api/general.rst index 361527d10..aaee300a6 100644 --- a/doc/development/api/general.rst +++ b/doc/development/api/general.rst @@ -13,7 +13,7 @@ Core .. automodule:: pretix.base.signals :members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types, item_copy_data, register_sales_channels, register_global_settings, quota_availability, global_email_filter, - register_ticket_secret_generators + register_ticket_secret_generators, gift_card_transaction_display Order events """""""""""" diff --git a/src/pretix/api/serializers/organizer.py b/src/pretix/api/serializers/organizer.py index 07a16623d..9e7413a43 100644 --- a/src/pretix/api/serializers/organizer.py +++ b/src/pretix/api/serializers/organizer.py @@ -160,6 +160,7 @@ class FlexibleTicketRelatedField(serializers.PrimaryKeyRelatedField): class GiftCardSerializer(I18nAwareModelSerializer): value = serializers.DecimalField(max_digits=13, decimal_places=2, min_value=Decimal('0.00')) owner_ticket = FlexibleTicketRelatedField(required=False, allow_null=True, queryset=OrderPosition.all.none()) + issuer = serializers.SlugRelatedField(slug_field='slug', read_only=True) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -196,7 +197,8 @@ class GiftCardSerializer(I18nAwareModelSerializer): class Meta: model = GiftCard - fields = ('id', 'secret', 'issuance', 'value', 'currency', 'testmode', 'expires', 'conditions', 'owner_ticket') + fields = ('id', 'secret', 'issuance', 'value', 'currency', 'testmode', 'expires', 'conditions', 'owner_ticket', + 'issuer') class OrderEventSlugField(serializers.RelatedField): @@ -207,11 +209,12 @@ class OrderEventSlugField(serializers.RelatedField): class GiftCardTransactionSerializer(I18nAwareModelSerializer): order = serializers.SlugRelatedField(slug_field='code', read_only=True) + acceptor = serializers.SlugRelatedField(slug_field='slug', read_only=True) event = OrderEventSlugField(source='order', read_only=True) class Meta: model = GiftCardTransaction - fields = ('id', 'datetime', 'value', 'event', 'order', 'text') + fields = ('id', 'datetime', 'value', 'event', 'order', 'text', 'info', 'acceptor') class EventSlugField(serializers.SlugRelatedField): diff --git a/src/pretix/api/views/organizer.py b/src/pretix/api/views/organizer.py index 81561afee..d71721ed3 100644 --- a/src/pretix/api/views/organizer.py +++ b/src/pretix/api/views/organizer.py @@ -155,7 +155,9 @@ class GiftCardViewSet(viewsets.ModelViewSet): qs = self.request.organizer.accepted_gift_cards else: qs = self.request.organizer.issued_gift_cards.all() - return qs + return qs.prefetch_related( + 'issuer' + ) def get_serializer_context(self): ctx = super().get_serializer_context() @@ -166,7 +168,7 @@ class GiftCardViewSet(viewsets.ModelViewSet): def perform_create(self, serializer): value = serializer.validated_data.pop('value') inst = serializer.save(issuer=self.request.organizer) - inst.transactions.create(value=value) + inst.transactions.create(value=value, acceptor=self.request.organizer) inst.log_action( 'pretix.giftcards.transaction.manual', user=self.request.user, @@ -197,7 +199,7 @@ class GiftCardViewSet(viewsets.ModelViewSet): if 'value' in self.request.data and value is not None: old_value = serializer.instance.value diff = value - old_value - inst.transactions.create(value=diff) + inst.transactions.create(value=diff, acceptor=self.request.organizer) inst.log_action( 'pretix.giftcards.transaction.manual', user=self.request.user, @@ -217,11 +219,14 @@ class GiftCardViewSet(viewsets.ModelViewSet): text = serializers.CharField(allow_blank=True, allow_null=True).to_internal_value( request.data.get('text', '') ) + info = serializers.JSONField(required=False, allow_null=True).to_internal_value( + request.data.get('info', {}) + ) if gc.value + value < Decimal('0.00'): return Response({ 'value': ['The gift card does not have sufficient credit for this operation.'] }, status=status.HTTP_409_CONFLICT) - gc.transactions.create(value=value, text=text) + gc.transactions.create(value=value, text=text, info=info, acceptor=self.request.organizer) gc.log_action( 'pretix.giftcards.transaction.manual', user=self.request.user, @@ -249,7 +254,7 @@ class GiftCardTransactionViewSet(viewsets.ReadOnlyModelViewSet): return get_object_or_404(qs, pk=self.kwargs.get('giftcard')) def get_queryset(self): - return self.giftcard.transactions.select_related('order', 'order__event') + return self.giftcard.transactions.select_related('order', 'order__event').prefetch_related('acceptor') class TeamViewSet(viewsets.ModelViewSet): diff --git a/src/pretix/base/migrations/0239_giftcard_info.py b/src/pretix/base/migrations/0239_giftcard_info.py new file mode 100644 index 000000000..a8822af28 --- /dev/null +++ b/src/pretix/base/migrations/0239_giftcard_info.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.18 on 2023-05-11 11:02 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0238_giftcard_owner_ticket'), + ] + + operations = [ + migrations.AddField( + model_name='giftcardtransaction', + name='acceptor', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='gift_card_transactions', to='pretixbase.organizer'), + ), + migrations.AddField( + model_name='giftcardtransaction', + name='info', + field=models.JSONField(null=True), + ), + ] diff --git a/src/pretix/base/models/giftcards.py b/src/pretix/base/models/giftcards.py index 470b7bb21..387ca6bf2 100644 --- a/src/pretix/base/models/giftcards.py +++ b/src/pretix/base/models/giftcards.py @@ -25,7 +25,9 @@ from django.conf import settings from django.core.validators import RegexValidator from django.db import models from django.db.models import Sum +from django.urls import reverse from django.utils.crypto import get_random_string +from django.utils.html import format_html from django.utils.timezone import now from django.utils.translation import gettext_lazy as _, pgettext_lazy @@ -160,6 +162,61 @@ class GiftCardTransaction(models.Model): on_delete=models.PROTECT ) text = models.TextField(blank=True, null=True) + info = models.JSONField( + null=True, blank=True, + ) + acceptor = models.ForeignKey( + 'Organizer', + related_name='gift_card_transactions', + on_delete=models.PROTECT, + null=True, blank=True + ) class Meta: ordering = ("datetime",) + + def save(self, *args, **kwargs): + if not self.pk and not self.acceptor: + raise ValueError("`acceptor` should be set on all new gift card transactions.") + super().save(*args, **kwargs) + + def display(self, customer_facing=True): + from ..signals import gift_card_transaction_display + + for receiver, response in gift_card_transaction_display.send(self, transaction=self, customer_facing=customer_facing): + if response: + return response + + if self.order_id: + if not self.text: + if not customer_facing: + return format_html( + '{}', + reverse( + "control:event.order", + kwargs={ + "event": self.order.event.slug, + "organizer": self.order.event.organizer.slug, + "code": self.order.code, + } + ), + self.order.full_code + ) + return self.order.full_code + else: + return self.text + else: + if self.text: + return format_html( + '{}: {}', + _('Manual transaction'), + self.text, + ) + else: + return _('Manual transaction') + + def display_backend(self): + return self.display(customer_facing=False) + + def display_presale(self): + return self.display(customer_facing=True) diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index 39f2753ee..6e82227f0 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -1469,7 +1469,8 @@ class GiftCardPayment(BasePaymentProvider): trans = gc.transactions.create( value=-1 * payment.amount, order=payment.order, - payment=payment + payment=payment, + acceptor=self.event.organizer, ) payment.info_data = { 'gift_card': gc.pk, @@ -1490,7 +1491,8 @@ class GiftCardPayment(BasePaymentProvider): trans = gc.transactions.create( value=refund.amount, order=refund.order, - refund=refund + refund=refund, + acceptor=self.event.organizer, ) refund.info_data = { 'gift_card': gc.pk, diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index 301c4e135..8c7fb7751 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -235,7 +235,7 @@ def reactivate_order(order: Order, force: bool=False, user: User=None, auth=None for gc in position.issued_gift_cards.all(): gc = GiftCard.objects.select_for_update(of=OF_SELF).get(pk=gc.pk) - gc.transactions.create(value=position.price, order=order) + gc.transactions.create(value=position.price, order=order, acceptor=order.event.organizer) break for m in position.granted_memberships.all(): @@ -513,7 +513,7 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device ) ) else: - gc.transactions.create(value=-position.price, order=order) + gc.transactions.create(value=-position.price, order=order, acceptor=order.event.organizer) for m in position.granted_memberships.all(): m.canceled = True @@ -2186,7 +2186,7 @@ class OrderChangeManager: card=gc.secret )) else: - gc.transactions.create(value=-op.position.price, order=self.order) + gc.transactions.create(value=-op.position.price, order=self.order, acceptor=self.order.event.organizer) for m in op.position.granted_memberships.with_usages().all(): m.canceled = True @@ -2202,7 +2202,7 @@ class OrderChangeManager: card=gc.secret )) else: - gc.transactions.create(value=-opa.position.price, order=self.order) + gc.transactions.create(value=-opa.position.price, order=self.order, acceptor=self.order.event.organizer) for m in opa.granted_memberships.with_usages().all(): m.canceled = True @@ -2918,7 +2918,7 @@ def signal_listener_issue_giftcards(sender: Event, order: Order, **kwargs): currency=sender.currency, issued_in=p, testmode=order.testmode, expires=sender.organizer.default_gift_card_expiry, ) - gc.transactions.create(value=p.price - issued, order=order) + gc.transactions.create(value=p.price - issued, order=order, acceptor=sender.organizer) any_giftcards = True p.secret = gc.secret p.save(update_fields=['secret']) diff --git a/src/pretix/base/signals.py b/src/pretix/base/signals.py index 859799f0d..67d4020db 100644 --- a/src/pretix/base/signals.py +++ b/src/pretix/base/signals.py @@ -578,6 +578,20 @@ All plugins that are installed may send fields for the global settings form, as an OrderedDict of (setting name, form field). """ +gift_card_transaction_display = django.dispatch.Signal() +""" +Arguments: ``transaction``, ``customer_facing`` + +To display an instance of the ``GiftCardTransaction`` model to a human user, +``pretix.base.signals.gift_card_transaction_display`` will be sent out with a ``transaction`` argument. +The ``customer_facing`` argument specifies whether the HTML will be shown to an end-user or if it is being +used in the backend. + +The first received response that is not ``None`` will be used to display the log entry +to the user. The receivers are expected to return a string (that might be marked with ``mark_safe`` from Django if +it contains HTML). +""" + order_fee_calculation = EventPluginSignal() """ Arguments: ``positions``, ``invoice_address``, ``meta_info``, ``total``, ``gift_cards``, ``payment_requests`` diff --git a/src/pretix/control/templates/pretixcontrol/organizers/giftcard.html b/src/pretix/control/templates/pretixcontrol/organizers/giftcard.html index 032d65f70..13aa41507 100644 --- a/src/pretix/control/templates/pretixcontrol/organizers/giftcard.html +++ b/src/pretix/control/templates/pretixcontrol/organizers/giftcard.html @@ -70,29 +70,32 @@ {% trans "Date" %} - {% trans "Order" %} + {% trans "Information" %} {% trans "Value" %} - {% for t in card.transactions.all %} + {% for t in transactions %} {{ t.datetime|date:"SHORT_DATETIME_FORMAT" }} - {% if t.order %} - - {{ t.order.full_code }} - - {% if t.refund and t.value > 0 and t.value <= card.value %} - - {% endif %} - {% else %} - {% trans "Manual transaction" %}{% if t.text %}: {{ t.text }}{% endif %} + {{ t.display_backend }} + {% if t.refund and t.value > 0 and t.value <= card.value %} + + {% endif %} + {% if staff_session and t.info %} +
{{ t.info|pprint }}
+ {% endif %} + {% if t.acceptor and t.acceptor != request.organizer %} + +
+ {{ t.acceptor }} +
{% endif %} diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index 7ed3734f6..239de7ee9 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -1452,6 +1452,7 @@ class GiftCardDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi self.object.transactions.create( value=value, text=request.POST.get('text') or None, + acceptor=request.organizer, ) self.object.log_action( 'pretix.giftcards.transaction.manual', @@ -1471,6 +1472,16 @@ class GiftCardDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi )) return self.get(request, *args, **kwargs) + def get_context_data(self, **kwargs): + return super().get_context_data( + **kwargs, + transactions=self.object.transactions.select_related( + 'order', 'order__event', 'order__event__organizer', 'payment', 'refund' + ).prefetch_related( + 'acceptor' + ) + ) + class GiftCardCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView): template_name = 'pretixcontrol/organizers/giftcard_create.html' @@ -1497,6 +1508,7 @@ class GiftCardCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi form.instance.issuer = self.request.organizer super().form_valid(form) form.instance.transactions.create( + acceptor=self.request.organizer, value=form.cleaned_data['value'] ) form.instance.log_action('pretix.giftcards.created', user=self.request.user, data={}) diff --git a/src/pretix/presale/templates/pretixpresale/event/fragment_giftcard_history.html b/src/pretix/presale/templates/pretixpresale/event/fragment_giftcard_history.html index 349d7e8f3..eaf6ffd52 100644 --- a/src/pretix/presale/templates/pretixpresale/event/fragment_giftcard_history.html +++ b/src/pretix/presale/templates/pretixpresale/event/fragment_giftcard_history.html @@ -4,7 +4,7 @@ {% trans "Date" %} - {% trans "Order" %} + {% trans "Information" %} {% trans "Value" %} @@ -13,11 +13,7 @@ {{ t.datetime|date:"SHORT_DATETIME_FORMAT" }} - {% if t.order %} - {{ t.order.full_code }} - {% else %} - {% if t.text %}{{ t.text }}{% else %}{% trans "Manual transaction" %}{% endif %} - {% endif %} + {{ t.display_presale }} {{ t.value|money:giftcard.currency }} diff --git a/src/tests/api/test_giftcards.py b/src/tests/api/test_giftcards.py index 61336942d..f204484b8 100644 --- a/src/tests/api/test_giftcards.py +++ b/src/tests/api/test_giftcards.py @@ -33,7 +33,7 @@ from pretix.base.models import GiftCard, Order, Organizer @pytest.fixture def giftcard(organizer, event): gc = organizer.issued_gift_cards.create(secret="ABCDEF", currency="EUR") - gc.transactions.create(value=Decimal('23.00')) + gc.transactions.create(value=Decimal('23.00'), acceptor=organizer) return gc @@ -53,6 +53,7 @@ TEST_GC_RES = { "expires": None, "conditions": None, "currency": "EUR", + "issuer": "dummy", "owner_ticket": None } @@ -264,7 +265,8 @@ def test_giftcard_transact(token_client, organizer, event, giftcard): '/api/v1/organizers/{}/giftcards/{}/transact/'.format(organizer.slug, giftcard.pk), { 'value': '10.00', - 'text': 'bla' + 'text': 'bla', + 'info': {"a": "b"} }, format='json' ) @@ -272,6 +274,23 @@ def test_giftcard_transact(token_client, organizer, event, giftcard): giftcard.refresh_from_db() assert giftcard.value == Decimal('43.00') assert giftcard.transactions.last().text == 'bla' + assert giftcard.transactions.last().info == {"a": "b"} + assert giftcard.transactions.last().acceptor == organizer + + +@pytest.mark.django_db +def test_giftcard_transact_cross_organizer(token_client, organizer, event, other_giftcard): + resp = token_client.post( + '/api/v1/organizers/{}/giftcards/{}/transact/?include_accepted=true'.format(organizer.slug, other_giftcard.pk), + { + 'value': '10.00', + }, + format='json' + ) + assert resp.status_code == 200 + other_giftcard.refresh_from_db() + assert other_giftcard.value == Decimal('10.00') + assert other_giftcard.transactions.last().acceptor == organizer @pytest.mark.django_db @@ -314,7 +333,9 @@ def test_giftcard_transactions(token_client, organizer, giftcard): "value": "23.00", "event": None, "order": None, - "text": None + "text": None, + "info": None, + "acceptor": organizer.slug } ] } diff --git a/src/tests/api/test_reusable_media.py b/src/tests/api/test_reusable_media.py index 57ab79f29..7bd58cb59 100644 --- a/src/tests/api/test_reusable_media.py +++ b/src/tests/api/test_reusable_media.py @@ -32,7 +32,7 @@ from pretix.base.models import Order, Organizer, ReusableMedium @pytest.fixture def giftcard(organizer): gc = organizer.issued_gift_cards.create(secret="ABCDEF", currency="EUR") - gc.transactions.create(value=Decimal('23.00')) + gc.transactions.create(value=Decimal('23.00'), acceptor=organizer) return gc @@ -50,7 +50,7 @@ def organizer2(): @pytest.fixture def giftcard2(organizer2): gc = organizer2.issued_gift_cards.create(secret="ABCDEF", currency="EUR") - gc.transactions.create(value=Decimal('23.00')) + gc.transactions.create(value=Decimal('23.00'), acceptor=organizer2) return gc @@ -190,6 +190,7 @@ def test_medium_detail(token_client, organizer, event, medium, giftcard, custome "expires": None, "conditions": None, "owner_ticket": resp.data["linked_orderposition"], + "issuer": "dummy", } diff --git a/src/tests/base/test_orders.py b/src/tests/base/test_orders.py index 0f24d8ce0..63b394880 100644 --- a/src/tests/base/test_orders.py +++ b/src/tests/base/test_orders.py @@ -965,7 +965,7 @@ class OrderCancelTests(TestCase): @classscope(attr='o') def test_auto_refund_possible_issued_giftcard(self): gc = self.o.issued_gift_cards.create(currency="EUR", issued_in=self.op1) - gc.transactions.create(value=23) + gc.transactions.create(value=23, acceptor=self.o) self.order.payments.create( amount=Decimal('46.00'), state=OrderPayment.PAYMENT_STATE_CONFIRMED, @@ -979,7 +979,7 @@ class OrderCancelTests(TestCase): @classscope(attr='o') def test_auto_refund_impossible_issued_giftcard_used(self): gc = self.o.issued_gift_cards.create(currency="EUR", issued_in=self.op1) - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.o) self.order.payments.create( amount=Decimal('46.00'), state=OrderPayment.PAYMENT_STATE_CONFIRMED, @@ -1397,7 +1397,7 @@ class OrderChangeManagerTests(TestCase): @classscope(attr='o') def test_cancel_issued_giftcard(self): gc = self.o.issued_gift_cards.create(currency="EUR", issued_in=self.op1) - gc.transactions.create(value=23) + gc.transactions.create(value=23, acceptor=self.o) self.ocm.cancel(self.op1) self.ocm.commit() assert gc.value == Decimal('0.00') @@ -1422,7 +1422,7 @@ class OrderChangeManagerTests(TestCase): @classscope(attr='o') def test_cancel_issued_giftcard_used(self): gc = self.o.issued_gift_cards.create(currency="EUR", issued_in=self.op1) - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.o) self.ocm.cancel(self.op1) with self.assertRaises(OrderError): self.ocm.commit() @@ -1430,7 +1430,7 @@ class OrderChangeManagerTests(TestCase): @classscope(attr='o') def test_change_price_issued_giftcard_used(self): gc = self.o.issued_gift_cards.create(currency="EUR", issued_in=self.op1) - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.o) with self.assertRaises(OrderError): self.ocm.change_price(self.op1, 25) @@ -3181,9 +3181,9 @@ def test_giftcard_multiple(event): item=ticket, price=23, expires=now() + timedelta(days=1), event=event, cart_id="123" ) gc1 = event.organizer.issued_gift_cards.create(currency="EUR") - gc1.transactions.create(value=12) + gc1.transactions.create(value=12, acceptor=event.organizer) gc2 = event.organizer.issued_gift_cards.create(currency="EUR") - gc2.transactions.create(value=12) + gc2.transactions.create(value=12, acceptor=event.organizer) order = _create_order( event, email='dummy@example.org', positions=[cp1], now_dt=now(), @@ -3233,7 +3233,7 @@ def test_giftcard_partial(event): item=ticket, price=23, expires=now() + timedelta(days=1), event=event, cart_id="123" ) gc1 = event.organizer.issued_gift_cards.create(currency="EUR") - gc1.transactions.create(value=12) + gc1.transactions.create(value=12, acceptor=event.organizer) order = _create_order( event, email='dummy@example.org', positions=[cp1], now_dt=now(), @@ -3280,7 +3280,7 @@ def test_giftcard_payment_fee(event): item=ticket, price=23, expires=now() + timedelta(days=1), event=event, cart_id="123" ) gc1 = event.organizer.issued_gift_cards.create(currency="EUR") - gc1.transactions.create(value=12) + gc1.transactions.create(value=12, acceptor=event.organizer) order = _create_order( event, email='dummy@example.org', positions=[cp1], now_dt=now(), @@ -3326,7 +3326,7 @@ def test_giftcard_invalid_currency(event): item=ticket, price=23, expires=now() + timedelta(days=1), event=event, cart_id="123" ) gc1 = event.organizer.issued_gift_cards.create(currency="USD") - gc1.transactions.create(value=12) + gc1.transactions.create(value=12, acceptor=event.organizer) _create_order( event, email='dummy@example.org', positions=[cp1], now_dt=now(), @@ -3368,7 +3368,7 @@ def test_giftcard_invalid_organizer(event): ) o2 = Organizer.objects.create(slug="foo", name="bar") gc1 = o2.issued_gift_cards.create(currency="EUR") - gc1.transactions.create(value=12) + gc1.transactions.create(value=12, acceptor=event.organizer) _create_order( event, email='dummy@example.org', positions=[cp1], now_dt=now(), @@ -3409,7 +3409,7 @@ def test_giftcard_test_mode_invalid(event): item=ticket, price=23, expires=now() + timedelta(days=1), event=event, cart_id="123" ) gc1 = event.organizer.issued_gift_cards.create(currency="EUR", testmode=True) - gc1.transactions.create(value=12) + gc1.transactions.create(value=12, acceptor=event.organizer) _create_order( event, email='dummy@example.org', positions=[cp1], now_dt=now(), @@ -3452,7 +3452,7 @@ def test_giftcard_test_mode_event(event): event.testmode = True event.save() gc1 = event.organizer.issued_gift_cards.create(currency="EUR", testmode=False) - gc1.transactions.create(value=12) + gc1.transactions.create(value=12, acceptor=event.organizer) _create_order( event, email='dummy@example.org', positions=[cp1], now_dt=now(), @@ -3493,7 +3493,7 @@ def test_giftcard_swap(event): item=ticket, price=23, expires=now() + timedelta(days=1), event=event, cart_id="123" ) gc1 = event.organizer.issued_gift_cards.create(currency="EUR", testmode=False) - gc1.transactions.create(value=12) + gc1.transactions.create(value=12, acceptor=event.organizer) _create_order( event, email='dummy@example.org', positions=[cp1], now_dt=now(), diff --git a/src/tests/control/test_giftcards.py b/src/tests/control/test_giftcards.py index fd0f0bc70..949d875fa 100644 --- a/src/tests/control/test_giftcards.py +++ b/src/tests/control/test_giftcards.py @@ -44,7 +44,7 @@ def organizer2(): @pytest.fixture def gift_card(organizer): gc = organizer.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=42) + gc.transactions.create(value=42, acceptor=organizer) return gc @@ -140,7 +140,7 @@ def test_card_detail_view_transact_revert_refund(organizer, admin_user, gift_car r = o.refunds.create( amount=o.total, provider='giftcard', state=OrderRefund.REFUND_STATE_DONE ) - t = gift_card.transactions.create(value=14, order=o, refund=r) + t = gift_card.transactions.create(value=14, order=o, refund=r, acceptor=organizer) client.login(email='dummy@dummy.dummy', password='dummy') r = client.post('/control/organizer/dummy/giftcard/{}/'.format(gift_card.pk), { diff --git a/src/tests/control/test_reusable_media.py b/src/tests/control/test_reusable_media.py index ee028e304..181018813 100644 --- a/src/tests/control/test_reusable_media.py +++ b/src/tests/control/test_reusable_media.py @@ -44,7 +44,7 @@ def medium(organizer): @pytest.fixture def gift_card(organizer): gc = organizer.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=42) + gc.transactions.create(value=42, acceptor=organizer) return gc diff --git a/src/tests/presale/test_checkout.py b/src/tests/presale/test_checkout.py index 1730c0b8f..a1a9d19e9 100644 --- a/src/tests/presale/test_checkout.py +++ b/src/tests/presale/test_checkout.py @@ -1440,7 +1440,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): def test_giftcard_partial(self): gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.orga) self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): @@ -1483,9 +1483,9 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): def test_giftcard_full_with_multiple(self): gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.orga) gc2 = self.orga.issued_gift_cards.create(currency="EUR") - gc2.transactions.create(value=20) + gc2.transactions.create(value=20, acceptor=self.orga) self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): @@ -1526,7 +1526,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): def test_giftcard_full(self): gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=30) + gc.transactions.create(value=30, acceptor=self.orga) self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): @@ -1555,7 +1555,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): def test_giftcard_racecondition(self): gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.orga) self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): @@ -1584,7 +1584,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): assert '€20.00' in response.content.decode() assert '3.00' in response.content.decode() - gc.transactions.create(value=-2) + gc.transactions.create(value=-2, acceptor=self.orga) response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True) doc = BeautifulSoup(response.content.decode(), "lxml") @@ -1601,7 +1601,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): def test_giftcard_expired(self): gc = self.orga.issued_gift_cards.create(currency="EUR", expires=now() - timedelta(days=1)) - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.orga) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( @@ -1616,7 +1616,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): def test_giftcard_invalid_currency(self): gc = self.orga.issued_gift_cards.create(currency="USD") - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.orga) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( @@ -1633,7 +1633,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): self.orga.issued_gift_cards.create(currency="EUR") orga2 = Organizer.objects.create(slug="foo2", name="foo2") gc = orga2.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.orga) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( @@ -1650,7 +1650,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): self.orga.issued_gift_cards.create(currency="EUR") orga2 = Organizer.objects.create(slug="foo2", name="foo2") gc = orga2.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=23) + gc.transactions.create(value=23, acceptor=orga2) self.orga.gift_card_issuer_acceptance.create(issuer=orga2) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): @@ -1670,11 +1670,15 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): self.assertEqual(len(doc.select(".thank-you")), 1) with scopes_disabled(): o = Order.objects.last() - assert o.payments.get(provider='giftcard').amount == Decimal('23.00') + p = o.payments.get(provider='giftcard') + assert p.amount == Decimal('23.00') + gc.refresh_from_db() + assert gc.issuer == orga2 + assert gc.transactions.last().acceptor == self.orga def test_giftcard_in_test_mode(self): gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.orga) self.event.settings.set('payment_banktransfer__enabled', True) self.event.testmode = True self.event.save() @@ -1691,7 +1695,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): def test_giftcard_not_in_test_mode(self): gc = self.orga.issued_gift_cards.create(currency="EUR", testmode=True) - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.orga) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( @@ -1720,7 +1724,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): def test_giftcard_twice(self): gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.orga) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): CartPosition.objects.create( @@ -1739,7 +1743,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): def test_giftcard_swap(self): gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.orga) self.event.settings.set('payment_banktransfer__enabled', True) self.ticket.issue_giftcard = True self.ticket.save() @@ -1756,7 +1760,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): def test_giftcard_like_method_with_min_value(self): gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=20) + gc.transactions.create(value=20, acceptor=self.orga) self.event.settings.set('payment_stripe__enabled', True) self.event.settings.set('payment_banktransfer__enabled', True) with scopes_disabled(): @@ -1790,7 +1794,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): # Our built-in gift card payment does not actually support setting a payment fee, but we still want to # test the core behavior in case a gift-card plugin does gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=27) + gc.transactions.create(value=27, acceptor=self.orga) self.event.settings.set('payment_giftcard__fee_percent', 10) self.event.settings.set('payment_giftcard__fee_reverse_calc', False) with scopes_disabled(): @@ -1829,7 +1833,7 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase): # Our built-in gift card payment does not actually support setting a payment fee, but we still want to # test the core behavior in case a gift-card plugin does gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=23) + gc.transactions.create(value=23, acceptor=self.orga) self.event.settings.set('payment_banktransfer__enabled', True) self.event.settings.set('payment_banktransfer__fee_percent', 20) self.event.settings.set('payment_banktransfer__fee_reverse_calc', False) diff --git a/src/tests/presale/test_orders.py b/src/tests/presale/test_orders.py index 9783a573b..d2b88c759 100644 --- a/src/tests/presale/test_orders.py +++ b/src/tests/presale/test_orders.py @@ -1276,7 +1276,7 @@ class OrdersTest(BaseOrdersTest): amount=Decimal('10.00'), ) gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=10) + gc.transactions.create(value=10, acceptor=self.orga) response = self.client.get( '/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), ) @@ -1315,7 +1315,7 @@ class OrdersTest(BaseOrdersTest): amount=Decimal('10.00'), ) gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=10) + gc.transactions.create(value=10, acceptor=self.orga) self.ticket.issue_giftcard = True self.ticket.save() response = self.client.post( @@ -1330,7 +1330,7 @@ class OrdersTest(BaseOrdersTest): def test_change_paymentmethod_giftcard_wrong_currency(self): with scopes_disabled(): gc = self.orga.issued_gift_cards.create(currency="USD") - gc.transactions.create(value=10) + gc.transactions.create(value=10, acceptor=self.orga) response = self.client.post( '/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), { @@ -1345,7 +1345,7 @@ class OrdersTest(BaseOrdersTest): self.order.testmode = True self.order.save() gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=10) + gc.transactions.create(value=10, acceptor=self.orga) response = self.client.post( '/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), { @@ -1358,7 +1358,7 @@ class OrdersTest(BaseOrdersTest): def test_change_paymentmethod_giftcard_not_in_test_mode(self): with scopes_disabled(): gc = self.orga.issued_gift_cards.create(currency="EUR", testmode=True) - gc.transactions.create(value=10) + gc.transactions.create(value=10, acceptor=self.orga) response = self.client.post( '/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), { @@ -1385,7 +1385,7 @@ class OrdersTest(BaseOrdersTest): o = Organizer.objects.create(slug='Foo', name='bar') self.orga.issued_gift_cards.create(currency="EUR") gc = o.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=10) + gc.transactions.create(value=10, acceptor=self.orga) response = self.client.post( '/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), { @@ -1403,7 +1403,7 @@ class OrdersTest(BaseOrdersTest): amount=Decimal('10.00'), ) gc = self.orga.issued_gift_cards.create(currency="EUR") - gc.transactions.create(value=100) + gc.transactions.create(value=100, acceptor=self.orga) response = self.client.get( '/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), )