diff --git a/doc/development/api/payment.rst b/doc/development/api/payment.rst index 92d5614b98..9003cbd2b2 100644 --- a/doc/development/api/payment.rst +++ b/doc/development/api/payment.rst @@ -87,6 +87,10 @@ The provider class .. automethod:: order_control_render + .. automethod:: order_control_refund_render + + .. automethod:: order_control_refund_perform + Additional views ---------------- diff --git a/src/pretix/base/models.py b/src/pretix/base/models.py index a9e24d15fc..9a6b0510bb 100644 --- a/src/pretix/base/models.py +++ b/src/pretix/base/models.py @@ -1555,6 +1555,16 @@ class Order(Versionable): return True return False # nothing there to modify + def mark_refunded(self): + """ + Mark this order as refunded. This clones the order object, sets the payment status and + returns the cloned order object. + """ + order = self.clone() + order.status = Order.STATUS_REFUNDED + order.save() + return order + def mark_paid(self, provider=None, info=None, date=None, manual=None): """ Mark this order as paid. This clones the order object, sets the payment provider, diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index 86edb42a2a..1234fd9504 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -1,6 +1,7 @@ from collections import OrderedDict from decimal import Decimal from django import forms +from django.contrib import messages from django.forms import Form from django.http import HttpRequest @@ -299,3 +300,38 @@ class BasePaymentProvider: :param order: The order object """ return _('Payment provider: %s' % self.verbose_name) + + def order_control_refund_render(self, order: Order) -> str: + """ + Will be called if the event administrator clicks an order's 'refund' button. + This can be used to display information *before* the order is being refunded. + + It should return HTML code which should be displayed to the user. It should + contain information about to which extend the money will be refunded + automatically. + + :param order: The order object + """ + return '
{% blocktrans trimmed %} + Do you really want to refund this order? You cannot revert this action. + {% endblocktrans %}
+ + {{ payment|safe }} + + +{% endblock %} diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py index 2580dffb52..2eb69d7d68 100644 --- a/src/pretix/control/views/orders.py +++ b/src/pretix/control/views/orders.py @@ -40,19 +40,19 @@ class OrderView(DetailView): def order(self): return self.get_object() - -class OrderDetail(EventPermissionRequiredMixin, OrderView): - template_name = 'pretixcontrol/order/index.html' - permission = 'can_view_orders' - @cached_property def payment_provider(self): responses = register_payment_providers.send(self.request.event) for receiver, response in responses: provider = response(self.request.event) - if provider.identifier == self.object.payment_provider: + if provider.identifier == self.order.payment_provider: return provider + +class OrderDetail(EventPermissionRequiredMixin, OrderView): + template_name = 'pretixcontrol/order/index.html' + permission = 'can_view_orders' + def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['items'] = self.get_items() @@ -118,6 +118,10 @@ class OrderTransition(EventPermissionRequiredMixin, OrderView): order.payment_manual = True order.save() messages.success(self.request, _('The order has been marked as not paid.')) + elif self.order.status == 'p' and to == 'r': + ret = self.payment_provider.order_control_refund_perform(self.request, self.order) + if ret: + return redirect(ret) return redirect(reverse( 'control:event.order', kwargs={ @@ -134,14 +138,9 @@ class OrderTransition(EventPermissionRequiredMixin, OrderView): 'order': self.order, }) elif self.order.status == 'p' and to == 'r': - messages.error(self.request, _('Refunding orders is not yet implemented.')) - return redirect(reverse( - 'control:event.order', - kwargs={ - 'event': self.request.event.slug, - 'organizer': self.request.event.organizer.slug, - 'code': self.order.code, - } - )) + return render(self.request, 'pretixcontrol/order/refund.html', { + 'order': self.order, + 'payment': self.payment_provider.order_control_refund_render(self.order), + }) else: return HttpResponse(status=405) diff --git a/src/pretix/plugins/paypal/payment.py b/src/pretix/plugins/paypal/payment.py index 1e70c790c4..3a639aeaa6 100644 --- a/src/pretix/plugins/paypal/payment.py +++ b/src/pretix/plugins/paypal/payment.py @@ -200,3 +200,37 @@ class Paypal(BasePaymentProvider): ctx = {'request': request, 'event': self.event, 'settings': self.settings, 'payment_info': payment_info, 'order': order} return template.render(ctx) + + def order_control_refund_render(self, order) -> str: + return '{% blocktrans trimmed %} This order has been paid via PayPal. {% endblocktrans %}
+ {% elif order.status == "r" %} +{% blocktrans trimmed %} + This order has been planned to be paid via PayPal and has been marked as refunded. + {% endblocktrans %}
{% else %}{% blocktrans trimmed %} This order has been planned to be paid via PayPal, but the payment has not yet been completed. diff --git a/src/pretix/plugins/stripe/payment.py b/src/pretix/plugins/stripe/payment.py index 5d9d3f8537..2f3a62b2fa 100644 --- a/src/pretix/plugins/stripe/payment.py +++ b/src/pretix/plugins/stripe/payment.py @@ -91,3 +91,33 @@ class Stripe(BasePaymentProvider): ctx = {'request': request, 'event': self.event, 'settings': self.settings, 'payment_info': payment_info, 'order': order} return template.render(ctx) + + def order_control_refund_render(self, order) -> str: + return '
{% blocktrans trimmed %} This order has been paid via Stripe. {% endblocktrans %}
+ {% elif order.status == "p" %} +{% blocktrans trimmed %} + This order has been planned to be paid via Stripe and has been marked as refunded. + {% endblocktrans %}
{% else %}{% blocktrans trimmed %} This order has been planned to be paid via Stripe, but the payment has not yet been completed.