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:
|
except TypeError:
|
||||||
return None
|
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
|
@property
|
||||||
def pending_sum(self):
|
def pending_sum(self):
|
||||||
total = self.total
|
total = self.total
|
||||||
|
|||||||
@@ -375,9 +375,11 @@ class OrderRefundForm(forms.Form):
|
|||||||
self.order = kwargs.pop('order')
|
self.order = kwargs.pop('order')
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
change_decimal_field(self.fields['partial_amount'], self.order.event.currency)
|
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):
|
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')
|
val = self.cleaned_data.get('partial_amount')
|
||||||
if val is not None and (val > max_amount or val <= 0):
|
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))
|
raise ValidationError(_('The refund amount needs to be positive and less than {}.').format(max_amount))
|
||||||
|
|||||||
@@ -50,10 +50,7 @@
|
|||||||
{% trans "Cancel order" %}
|
{% trans "Cancel order" %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if order.status == 'p' %}
|
{% if order.payment_refund_sum > 0 %}
|
||||||
<button name="status" value="n" class="btn btn-default">{% trans "Mark as not paid" %}</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if overpaid|add:order.total != 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">
|
<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" %}
|
{% trans "Create a refund" %}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -35,10 +35,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<p> </p>
|
<p> </p>
|
||||||
<fieldset class="form-inline form-order-change">
|
{% if form.action %}
|
||||||
<legend>{% trans "What should happen to the order?" %}</legend>
|
<fieldset class="form-inline form-order-change">
|
||||||
{% bootstrap_field form.action layout='horizontal' horizontal_label_class='sr-only' horizontal_field_class='' %}
|
<legend>{% trans "What should happen to the order?" %}</legend>
|
||||||
</fieldset>
|
{% bootstrap_field form.action layout='horizontal' horizontal_label_class='sr-only' horizontal_field_class='' %}
|
||||||
|
</fieldset>
|
||||||
|
{% endif %}
|
||||||
<p> </p>
|
<p> </p>
|
||||||
|
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|||||||
@@ -442,7 +442,7 @@ class OrderRefundView(OrderView):
|
|||||||
data=self.request.POST if self.request.method == "POST" else None,
|
data=self.request.POST if self.request.method == "POST" else None,
|
||||||
prefix='start',
|
prefix='start',
|
||||||
initial={
|
initial={
|
||||||
'partial_amount': self.order.total - self.order.pending_sum,
|
'partial_amount': self.order.payment_refund_sum,
|
||||||
'action': (
|
'action': (
|
||||||
'mark_pending' if self.order.status == Order.STATUS_PAID
|
'mark_pending' if self.order.status == Order.STATUS_PAID
|
||||||
else 'do_nothing'
|
else 'do_nothing'
|
||||||
@@ -465,7 +465,7 @@ class OrderRefundView(OrderView):
|
|||||||
|
|
||||||
# Algorithm to choose which payments are to be refunded to create the least hassle
|
# Algorithm to choose which payments are to be refunded to create the least hassle
|
||||||
if self.start_form.cleaned_data.get('mode') == 'full':
|
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:
|
else:
|
||||||
to_refund = full_refund = self.start_form.cleaned_data.get('partial_amount')
|
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 charge_retr(*args, **kwargs):
|
||||||
def refund_create(amount):
|
def refund_create(amount):
|
||||||
pass
|
r = MockedCharge()
|
||||||
|
r.id = 'foo'
|
||||||
|
r.status = 'succeeded'
|
||||||
|
return r
|
||||||
|
|
||||||
c = MockedCharge()
|
c = MockedCharge()
|
||||||
c.refunds.create = refund_create
|
c.refunds.create = refund_create
|
||||||
|
|||||||
Reference in New Issue
Block a user