forked from CGM_Public/pretix_original
Properly restrict refunds to full payment amount
This commit is contained in:
@@ -188,6 +188,17 @@ class Order(LoggedModel):
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def payment_refund_sum(self):
|
||||
payment_sum = self.payments.filter(
|
||||
state__in=(OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_REFUNDED)
|
||||
).aggregate(s=Sum('amount'))['s'] or Decimal('0.00')
|
||||
refund_sum = self.refunds.filter(
|
||||
state__in=(OrderRefund.REFUND_STATE_DONE, OrderRefund.REFUND_STATE_TRANSIT,
|
||||
OrderRefund.REFUND_STATE_CREATED)
|
||||
).aggregate(s=Sum('amount'))['s'] or Decimal('0.00')
|
||||
return payment_sum - refund_sum
|
||||
|
||||
@property
|
||||
def pending_sum(self):
|
||||
total = self.total
|
||||
|
||||
@@ -375,9 +375,11 @@ class OrderRefundForm(forms.Form):
|
||||
self.order = kwargs.pop('order')
|
||||
super().__init__(*args, **kwargs)
|
||||
change_decimal_field(self.fields['partial_amount'], self.order.event.currency)
|
||||
if self.order.status in (Order.STATUS_REFUNDED, Order.STATUS_CANCELED):
|
||||
del self.fields['action']
|
||||
|
||||
def clean_partial_amount(self):
|
||||
max_amount = self.order.total - self.order.pending_sum
|
||||
max_amount = self.order.payment_refund_sum
|
||||
val = self.cleaned_data.get('partial_amount')
|
||||
if val is not None and (val > max_amount or val <= 0):
|
||||
raise ValidationError(_('The refund amount needs to be positive and less than {}.').format(max_amount))
|
||||
|
||||
@@ -50,10 +50,7 @@
|
||||
{% trans "Cancel order" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if order.status == 'p' %}
|
||||
<button name="status" value="n" class="btn btn-default">{% trans "Mark as not paid" %}</button>
|
||||
{% endif %}
|
||||
{% if overpaid|add:order.total != 0 %}
|
||||
{% if order.payment_refund_sum > 0 %}
|
||||
<a href="{% url "control:event.order.refunds.start" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}" class="btn btn-default">
|
||||
{% trans "Create a refund" %}
|
||||
</a>
|
||||
|
||||
@@ -35,10 +35,12 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
<p> </p>
|
||||
<fieldset class="form-inline form-order-change">
|
||||
<legend>{% trans "What should happen to the order?" %}</legend>
|
||||
{% bootstrap_field form.action layout='horizontal' horizontal_label_class='sr-only' horizontal_field_class='' %}
|
||||
</fieldset>
|
||||
{% if form.action %}
|
||||
<fieldset class="form-inline form-order-change">
|
||||
<legend>{% trans "What should happen to the order?" %}</legend>
|
||||
{% bootstrap_field form.action layout='horizontal' horizontal_label_class='sr-only' horizontal_field_class='' %}
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
<p> </p>
|
||||
|
||||
{% csrf_token %}
|
||||
|
||||
@@ -442,7 +442,7 @@ class OrderRefundView(OrderView):
|
||||
data=self.request.POST if self.request.method == "POST" else None,
|
||||
prefix='start',
|
||||
initial={
|
||||
'partial_amount': self.order.total - self.order.pending_sum,
|
||||
'partial_amount': self.order.payment_refund_sum,
|
||||
'action': (
|
||||
'mark_pending' if self.order.status == Order.STATUS_PAID
|
||||
else 'do_nothing'
|
||||
@@ -465,7 +465,7 @@ class OrderRefundView(OrderView):
|
||||
|
||||
# Algorithm to choose which payments are to be refunded to create the least hassle
|
||||
if self.start_form.cleaned_data.get('mode') == 'full':
|
||||
to_refund = full_refund = self.order.total - self.order.pending_sum
|
||||
to_refund = full_refund = self.order.payment_refund_sum
|
||||
else:
|
||||
to_refund = full_refund = self.start_form.cleaned_data.get('partial_amount')
|
||||
|
||||
|
||||
@@ -1402,7 +1402,10 @@ def test_refund_paid_order_automatically(client, env, monkeypatch):
|
||||
|
||||
def charge_retr(*args, **kwargs):
|
||||
def refund_create(amount):
|
||||
pass
|
||||
r = MockedCharge()
|
||||
r.id = 'foo'
|
||||
r.status = 'succeeded'
|
||||
return r
|
||||
|
||||
c = MockedCharge()
|
||||
c.refunds.create = refund_create
|
||||
|
||||
Reference in New Issue
Block a user