mirror of
https://github.com/pretix/pretix.git
synced 2026-08-07 10:17:49 +00:00
Allow to charge a cancellation fee on unpaid orders (#2845)
This commit is contained in:
@@ -760,6 +760,9 @@ class EventSettingsSerializer(SettingsSerializer):
|
||||
'invoice_logo_image',
|
||||
'cancel_allow_user',
|
||||
'cancel_allow_user_until',
|
||||
'cancel_allow_user_unpaid_keep',
|
||||
'cancel_allow_user_unpaid_keep_fees',
|
||||
'cancel_allow_user_unpaid_keep_percentage',
|
||||
'cancel_allow_user_paid',
|
||||
'cancel_allow_user_paid_until',
|
||||
'cancel_allow_user_paid_keep',
|
||||
|
||||
@@ -564,17 +564,30 @@ class Order(LockModel, LoggedModel):
|
||||
@cached_property
|
||||
def user_cancel_fee(self):
|
||||
fee = Decimal('0.00')
|
||||
if self.event.settings.cancel_allow_user_paid_keep_fees:
|
||||
fee += self.fees.filter(
|
||||
fee_type__in=(OrderFee.FEE_TYPE_PAYMENT, OrderFee.FEE_TYPE_SHIPPING, OrderFee.FEE_TYPE_SERVICE,
|
||||
OrderFee.FEE_TYPE_CANCELLATION)
|
||||
).aggregate(
|
||||
s=Sum('value')
|
||||
)['s'] or 0
|
||||
if self.event.settings.cancel_allow_user_paid_keep_percentage:
|
||||
fee += self.event.settings.cancel_allow_user_paid_keep_percentage / Decimal('100.0') * (self.total - fee)
|
||||
if self.event.settings.cancel_allow_user_paid_keep:
|
||||
fee += self.event.settings.cancel_allow_user_paid_keep
|
||||
if self.status == Order.STATUS_PAID:
|
||||
if self.event.settings.cancel_allow_user_paid_keep_fees:
|
||||
fee += self.fees.filter(
|
||||
fee_type__in=(OrderFee.FEE_TYPE_PAYMENT, OrderFee.FEE_TYPE_SHIPPING, OrderFee.FEE_TYPE_SERVICE,
|
||||
OrderFee.FEE_TYPE_CANCELLATION)
|
||||
).aggregate(
|
||||
s=Sum('value')
|
||||
)['s'] or 0
|
||||
if self.event.settings.cancel_allow_user_paid_keep_percentage:
|
||||
fee += self.event.settings.cancel_allow_user_paid_keep_percentage / Decimal('100.0') * (self.total - fee)
|
||||
if self.event.settings.cancel_allow_user_paid_keep:
|
||||
fee += self.event.settings.cancel_allow_user_paid_keep
|
||||
else:
|
||||
if self.event.settings.cancel_allow_user_unpaid_keep_fees:
|
||||
fee += self.fees.filter(
|
||||
fee_type__in=(OrderFee.FEE_TYPE_PAYMENT, OrderFee.FEE_TYPE_SHIPPING, OrderFee.FEE_TYPE_SERVICE,
|
||||
OrderFee.FEE_TYPE_CANCELLATION)
|
||||
).aggregate(
|
||||
s=Sum('value')
|
||||
)['s'] or 0
|
||||
if self.event.settings.cancel_allow_user_unpaid_keep_percentage:
|
||||
fee += self.event.settings.cancel_allow_user_unpaid_keep_percentage / Decimal('100.0') * (self.total - fee)
|
||||
if self.event.settings.cancel_allow_user_unpaid_keep:
|
||||
fee += self.event.settings.cancel_allow_user_unpaid_keep
|
||||
return round_decimal(min(fee, self.total), self.event.currency)
|
||||
|
||||
@property
|
||||
@@ -642,10 +655,12 @@ class Order(LockModel, LoggedModel):
|
||||
if self.user_cancel_deadline and now() > self.user_cancel_deadline:
|
||||
return False
|
||||
|
||||
if self.status == Order.STATUS_PAID or self.payment_refund_sum > Decimal('0.00'):
|
||||
if self.status == Order.STATUS_PAID:
|
||||
if self.total == Decimal('0.00'):
|
||||
return self.event.settings.cancel_allow_user
|
||||
return self.event.settings.cancel_allow_user_paid
|
||||
elif self.payment_refund_sum > Decimal('0.00'):
|
||||
return False
|
||||
elif self.status == Order.STATUS_PENDING:
|
||||
return self.event.settings.cancel_allow_user
|
||||
return False
|
||||
|
||||
@@ -462,9 +462,13 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
|
||||
f._calculate_tax()
|
||||
f.save()
|
||||
|
||||
if order.payment_refund_sum < cancellation_fee:
|
||||
raise OrderError(_('The cancellation fee cannot be higher than the payment credit of this order.'))
|
||||
order.status = Order.STATUS_PAID
|
||||
if cancellation_fee > order.total:
|
||||
raise OrderError(_('The cancellation fee cannot be higher than the total amount of this order.'))
|
||||
elif order.payment_refund_sum < cancellation_fee:
|
||||
order.status = Order.STATUS_PENDING
|
||||
order.set_expires()
|
||||
else:
|
||||
order.status = Order.STATUS_PAID
|
||||
order.total = cancellation_fee
|
||||
order.cancellation_date = now()
|
||||
order.save(update_fields=['status', 'cancellation_date', 'total'])
|
||||
@@ -1097,8 +1101,16 @@ def expire_orders(sender, **kwargs):
|
||||
event_id = None
|
||||
expire = None
|
||||
|
||||
for o in Order.objects.filter(expires__lt=now(), status=Order.STATUS_PENDING,
|
||||
require_approval=False).select_related('event').order_by('event_id'):
|
||||
qs = Order.objects.filter(
|
||||
expires__lt=now(),
|
||||
status=Order.STATUS_PENDING,
|
||||
require_approval=False
|
||||
).exclude(
|
||||
Exists(
|
||||
OrderFee.objects.filter(order_id=OuterRef('pk'), fee_type=OrderFee.FEE_TYPE_CANCELLATION)
|
||||
)
|
||||
).select_related('event').order_by('event_id')
|
||||
for o in qs:
|
||||
if o.event_id != event_id:
|
||||
expire = o.event.settings.get('payment_term_expire_automatically', as_type=bool)
|
||||
event_id = o.event_id
|
||||
|
||||
@@ -1421,6 +1421,45 @@ DEFAULTS = {
|
||||
label=_("Customers can cancel their unpaid orders"),
|
||||
)
|
||||
},
|
||||
'cancel_allow_user_unpaid_keep': {
|
||||
'default': '0.00',
|
||||
'type': Decimal,
|
||||
'form_class': forms.DecimalField,
|
||||
'serializer_class': serializers.DecimalField,
|
||||
'serializer_kwargs': dict(
|
||||
max_digits=10, decimal_places=2
|
||||
),
|
||||
'form_kwargs': dict(
|
||||
label=_("Charge a fixed cancellation fee"),
|
||||
help_text=_("Only affects orders pending payments, a cancellation fee for free orders is never charged. "
|
||||
"Note that it will be your responsibility to claim the cancellation fee from the user."),
|
||||
)
|
||||
},
|
||||
'cancel_allow_user_unpaid_keep_fees': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
'form_class': forms.BooleanField,
|
||||
'serializer_class': serializers.BooleanField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Charge payment, shipping and service fees"),
|
||||
help_text=_("Only affects orders pending payments, a cancellation fee for free orders is never charged. "
|
||||
"Note that it will be your responsibility to claim the cancellation fee from the user."),
|
||||
)
|
||||
},
|
||||
'cancel_allow_user_unpaid_keep_percentage': {
|
||||
'default': '0.00',
|
||||
'type': Decimal,
|
||||
'form_class': forms.DecimalField,
|
||||
'serializer_class': serializers.DecimalField,
|
||||
'serializer_kwargs': dict(
|
||||
max_digits=10, decimal_places=2
|
||||
),
|
||||
'form_kwargs': dict(
|
||||
label=_("Charge a percentual cancellation fee"),
|
||||
help_text=_("Only affects orders pending payments, a cancellation fee for free orders is never charged. "
|
||||
"Note that it will be your responsibility to claim the cancellation fee from the user."),
|
||||
)
|
||||
},
|
||||
'cancel_allow_user_until': {
|
||||
'default': None,
|
||||
'type': RelativeDateWrapper,
|
||||
|
||||
@@ -666,6 +666,9 @@ class CancelSettingsForm(SettingsForm):
|
||||
'cancel_allow_user_until',
|
||||
'cancel_allow_user_paid',
|
||||
'cancel_allow_user_paid_until',
|
||||
'cancel_allow_user_unpaid_keep',
|
||||
'cancel_allow_user_unpaid_keep_fees',
|
||||
'cancel_allow_user_unpaid_keep_percentage',
|
||||
'cancel_allow_user_paid_keep',
|
||||
'cancel_allow_user_paid_keep_fees',
|
||||
'cancel_allow_user_paid_keep_percentage',
|
||||
|
||||
@@ -158,7 +158,7 @@ class CancelForm(ForceQuotaConfirmationForm):
|
||||
localize=True,
|
||||
label=_('Keep a cancellation fee of'),
|
||||
help_text=_('If you keep a fee, all positions within this order will be canceled and the order will be reduced '
|
||||
'to a paid cancellation fee. Payment and shipping fees will be canceled as well, so include them '
|
||||
'to a cancellation fee. Payment and shipping fees will be canceled as well, so include them '
|
||||
'in your cancellation fee if you want to keep them. Please always enter a gross value, '
|
||||
'tax will be calculated automatically.'),
|
||||
)
|
||||
@@ -176,23 +176,19 @@ class CancelForm(ForceQuotaConfirmationForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
prs = self.instance.payment_refund_sum
|
||||
if prs > 0:
|
||||
change_decimal_field(self.fields['cancellation_fee'], self.instance.event.currency)
|
||||
self.fields['cancellation_fee'].widget.attrs['placeholder'] = floatformat(
|
||||
Decimal('0.00'),
|
||||
settings.CURRENCY_PLACES.get(self.instance.event.currency, 2)
|
||||
)
|
||||
self.fields['cancellation_fee'].max_value = prs
|
||||
else:
|
||||
del self.fields['cancellation_fee']
|
||||
change_decimal_field(self.fields['cancellation_fee'], self.instance.event.currency)
|
||||
self.fields['cancellation_fee'].widget.attrs['placeholder'] = floatformat(
|
||||
Decimal('0.00'),
|
||||
settings.CURRENCY_PLACES.get(self.instance.event.currency, 2)
|
||||
)
|
||||
self.fields['cancellation_fee'].max_value = self.instance.total
|
||||
if not self.instance.invoices.exists():
|
||||
del self.fields['cancel_invoice']
|
||||
|
||||
def clean_cancellation_fee(self):
|
||||
val = self.cleaned_data['cancellation_fee'] or Decimal('0.00')
|
||||
if val > self.instance.payment_refund_sum:
|
||||
raise ValidationError(_('The cancellation fee cannot be higher than the payment credit of this order.'))
|
||||
if val > self.instance.total:
|
||||
raise ValidationError(_('The cancellation fee cannot be higher than the total amount of this order.'))
|
||||
return val
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
<legend>{% trans "Unpaid or free orders" %}</legend>
|
||||
{% bootstrap_field form.cancel_allow_user layout="control" %}
|
||||
{% bootstrap_field form.cancel_allow_user_until layout="control" %}
|
||||
{% bootstrap_field form.cancel_allow_user_unpaid_keep layout="control" %}
|
||||
{% bootstrap_field form.cancel_allow_user_unpaid_keep_percentage layout="control" %}
|
||||
{% bootstrap_field form.cancel_allow_user_unpaid_keep_fees layout="control" %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Paid orders" %}</legend>
|
||||
|
||||
@@ -190,7 +190,13 @@
|
||||
</dd>
|
||||
{% if order.status == "n" %}
|
||||
<dt>{% trans "Expiry date" %}</dt>
|
||||
<dd>{{ order.expires|date:"SHORT_DATETIME_FORMAT" }}</dd>
|
||||
<dd>
|
||||
{{ order.expires|date:"SHORT_DATETIME_FORMAT" }}
|
||||
{% if has_cancellation_fee and request.event.settings.payment_term_expire_automatically %}
|
||||
<span class="fa fa-warning text-danger" data-toggle="tooltip"
|
||||
title="{% trans "This order will not expire automatically as it has an open cancellation fee." %}"></span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
{% endif %}
|
||||
{% if request.organizer.settings.customer_accounts %}
|
||||
<dt>{% trans "Customer account" %}</dt>
|
||||
|
||||
@@ -293,6 +293,7 @@ class OrderDetail(OrderView):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['items'] = self.get_items()
|
||||
ctx['has_cancellation_fee'] = any(f.fee_type == OrderFee.FEE_TYPE_CANCELLATION for f in ctx['items']['fees'])
|
||||
ctx['event'] = self.request.event
|
||||
ctx['payments'] = self.order.payments.order_by('-created')
|
||||
ctx['refunds'] = self.order.refunds.select_related('payment').order_by('-created')
|
||||
|
||||
@@ -459,17 +459,24 @@
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can cancel this order using the following button.
|
||||
{% endblocktrans %}
|
||||
{% if order.total != 0 and order.user_cancel_fee %}
|
||||
{% blocktrans trimmed with fee=order.user_cancel_fee|money:request.event.currency %}
|
||||
You can cancel this order. As per our cancellation policy, you will still be required
|
||||
to pay a cancellation fee of <strong>{{ fee }}</strong>.
|
||||
{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans trimmed %}
|
||||
You can cancel this order using the following button.
|
||||
{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% trans "This will invalidate all tickets in this order." %}
|
||||
</p>
|
||||
<p>
|
||||
<a href="{% eventurl event 'presale:event.order.cancel' secret=order.secret order=order.code %}"
|
||||
class="btn btn-danger">
|
||||
<span class="fa fa-remove" aria-hidden="true"></span>
|
||||
{% trans "Cancel order" %}
|
||||
</a>
|
||||
<a href="{% eventurl event 'presale:event.order.cancel' secret=order.secret order=order.code %}"
|
||||
class="btn btn-danger">
|
||||
<span class="fa fa-remove" aria-hidden="true"></span>
|
||||
{% trans "Cancel order" %}
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
@@ -50,7 +50,12 @@
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if not request.event.settings.cancel_allow_user_paid_require_approval or not request.event.settings.cancel_allow_user_paid_require_approval_fee_unknown %}
|
||||
{% if order.status == "n" and order.total != 0 and order.user_cancel_fee %}
|
||||
{% blocktrans trimmed with fee=order.user_cancel_fee|money:request.event.currency %}
|
||||
You can cancel this order. As per our cancellation policy, you will still be required
|
||||
to pay a cancellation fee of <strong>{{ fee }}</strong>.
|
||||
{% endblocktrans %}
|
||||
{% elif not request.event.settings.cancel_allow_user_paid_require_approval or not request.event.settings.cancel_allow_user_paid_require_approval_fee_unknown %}
|
||||
{% if request.event.settings.cancel_allow_user_paid_adjust_fees and order.status == "p" and order.total != 0 %}
|
||||
<p>
|
||||
{% if cancel_fee %}
|
||||
@@ -98,7 +103,7 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if refund_amount %}
|
||||
{% if refund_amount > 0 %}
|
||||
{% if request.event.settings.cancel_allow_user_paid_refund_as_giftcard == "manually" %}
|
||||
<strong>
|
||||
{% trans "The organizer will get in touch with you to clarify the details of your refund." %}
|
||||
|
||||
@@ -917,7 +917,10 @@ class OrderCancelDo(EventViewMixin, OrderDetailMixin, AsyncAction, View):
|
||||
auto_refund = self.request.event.settings.cancel_allow_user_paid_refund_as_giftcard != "manually"
|
||||
if self.order.total == Decimal('0.00'):
|
||||
fee = Decimal('0.00')
|
||||
elif 'cancel_fee' in request.POST and self.request.event.settings.cancel_allow_user_paid_adjust_fees:
|
||||
elif self.order.status == Order.STATUS_PENDING:
|
||||
auto_refund = False
|
||||
fee = self.order.user_cancel_fee
|
||||
elif 'cancel_fee' in request.POST and self.order.status == Order.STATUS_PAID and self.request.event.settings.cancel_allow_user_paid_adjust_fees:
|
||||
fee = fee or Decimal('0.00')
|
||||
fee_in = re.sub('[^0-9.,]', '', request.POST.get('cancel_fee'))
|
||||
try:
|
||||
|
||||
@@ -1107,11 +1107,23 @@ def test_order_mark_canceled_pending_fee_not_allowed(token_client, organizer, ev
|
||||
'/api/v1/organizers/{}/events/{}/orders/{}/mark_canceled/'.format(
|
||||
organizer.slug, event.slug, order.code
|
||||
), data={
|
||||
'cancellation_fee': '7.00'
|
||||
'cancellation_fee': '700.00'
|
||||
}
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'detail': 'The cancellation fee cannot be higher than the payment credit of this order.'}
|
||||
assert resp.data == {'detail': 'The cancellation fee cannot be higher than the total amount of this order.'}
|
||||
assert len(djmail.outbox) == 0
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/orders/{}/mark_canceled/'.format(
|
||||
organizer.slug, event.slug, order.code
|
||||
), data={
|
||||
'cancellation_fee': '7.00'
|
||||
}
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.data['status'] == Order.STATUS_PENDING
|
||||
assert len(djmail.outbox) == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
||||
@@ -1403,6 +1403,8 @@ class OrderTestCase(BaseQuotaTestCase):
|
||||
self.order.total = 48
|
||||
self.order.save()
|
||||
self.order = Order.objects.get(pk=self.order.pk)
|
||||
self.order.status = Order.STATUS_PAID
|
||||
self.order.save()
|
||||
assert self.order.user_cancel_fee == Decimal('0.00')
|
||||
|
||||
self.event.settings.cancel_allow_user_paid_keep = Decimal('2.50')
|
||||
@@ -1421,6 +1423,27 @@ class OrderTestCase(BaseQuotaTestCase):
|
||||
self.order = Order.objects.get(pk=self.order.pk)
|
||||
assert self.order.user_cancel_fee == Decimal('48.00')
|
||||
|
||||
self.order.status = Order.STATUS_PENDING
|
||||
self.order.save()
|
||||
self.order = Order.objects.get(pk=self.order.pk)
|
||||
assert self.order.user_cancel_fee == Decimal('0.00')
|
||||
|
||||
self.event.settings.cancel_allow_user_unpaid_keep = Decimal('2.50')
|
||||
self.order = Order.objects.get(pk=self.order.pk)
|
||||
assert self.order.user_cancel_fee == Decimal('2.50')
|
||||
|
||||
self.event.settings.cancel_allow_user_unpaid_keep_percentage = Decimal('5.0')
|
||||
self.order = Order.objects.get(pk=self.order.pk)
|
||||
assert self.order.user_cancel_fee == Decimal('4.90')
|
||||
|
||||
self.event.settings.cancel_allow_user_unpaid_keep_fees = True
|
||||
self.order = Order.objects.get(pk=self.order.pk)
|
||||
assert self.order.user_cancel_fee == Decimal('6.80')
|
||||
|
||||
self.event.settings.cancel_allow_user_unpaid_keep = Decimal('100.00')
|
||||
self.order = Order.objects.get(pk=self.order.pk)
|
||||
assert self.order.user_cancel_fee == Decimal('48.00')
|
||||
|
||||
@classscope(attr='o')
|
||||
def test_paid_order_underpaid(self):
|
||||
self.order.status = Order.STATUS_PAID
|
||||
|
||||
@@ -285,6 +285,21 @@ def test_expire_twice(event):
|
||||
assert o2.invoices.count() == 2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_expire_skipped_if_canceled_with_fee(event):
|
||||
o2 = Order.objects.create(
|
||||
code='FO2', event=event, email='dummy@dummy.test',
|
||||
status=Order.STATUS_PENDING, locale='en',
|
||||
datetime=now(), expires=now() - timedelta(days=10),
|
||||
total=12,
|
||||
)
|
||||
o2.fees.create(fee_type=OrderFee.FEE_TYPE_CANCELLATION, value=12)
|
||||
generate_invoice(o2)
|
||||
expire_orders(None)
|
||||
o2 = Order.objects.get(id=o2.id)
|
||||
assert o2.status == Order.STATUS_PENDING
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_expiring_auto_disabled(event):
|
||||
event.settings.set('payment_term_expire_automatically', False)
|
||||
|
||||
@@ -496,7 +496,7 @@ def test_order_cancel_pending_fee_too_high(client, env):
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
'status': 'c',
|
||||
'cancellation_fee': '6.00'
|
||||
'cancellation_fee': '26.00'
|
||||
})
|
||||
with scopes_disabled():
|
||||
o = Order.objects.get(id=env[2].id)
|
||||
@@ -507,7 +507,7 @@ def test_order_cancel_pending_fee_too_high(client, env):
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_cancel_unpaid_no_fees_allowed(client, env):
|
||||
def test_order_cancel_unpaid_fees_allowed(client, env):
|
||||
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
client.get('/control/event/dummy/dummy/orders/FOO/transition?status=c')
|
||||
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
|
||||
@@ -516,10 +516,10 @@ def test_order_cancel_unpaid_no_fees_allowed(client, env):
|
||||
})
|
||||
with scopes_disabled():
|
||||
o = Order.objects.get(id=env[2].id)
|
||||
assert o.positions.exists()
|
||||
assert not o.fees.exists()
|
||||
assert o.status == Order.STATUS_CANCELED
|
||||
assert o.total == Decimal('14.00')
|
||||
assert not o.positions.exists()
|
||||
assert o.fees.exists()
|
||||
assert o.status == Order.STATUS_PENDING
|
||||
assert o.total == Decimal('6.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
||||
@@ -460,30 +460,19 @@ class OrdersTest(BaseOrdersTest):
|
||||
r = self.order.cancellation_requests.get()
|
||||
assert r.cancellation_fee == Decimal('3.00')
|
||||
|
||||
def test_orders_cancel_unpaid_no_request(self):
|
||||
def test_orders_cancel_partially_paid_no_selfservice(self):
|
||||
self.order.status = Order.STATUS_PENDING
|
||||
self.order.save()
|
||||
with scopes_disabled():
|
||||
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
||||
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total - Decimal("1.00"),
|
||||
state=OrderPayment.PAYMENT_STATE_CONFIRMED)
|
||||
self.event.settings.cancel_allow_user_paid = True
|
||||
self.event.settings.cancel_allow_user_paid_keep = Decimal('3.00')
|
||||
self.event.settings.cancel_allow_user_paid_require_approval = True
|
||||
response = self.client.get(
|
||||
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
||||
)
|
||||
assert response.status_code == 200
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.status == Order.STATUS_CANCELED
|
||||
with scopes_disabled():
|
||||
assert not self.order.refunds.exists()
|
||||
assert not self.order.cancellation_requests.exists()
|
||||
assert response.status_code == 302
|
||||
|
||||
def test_orders_cancel_free_no_request(self):
|
||||
self.order.status = Order.STATUS_PAID
|
||||
@@ -601,6 +590,33 @@ class OrdersTest(BaseOrdersTest):
|
||||
assert r.provider == "giftcard"
|
||||
assert r.amount == Decimal('20.00')
|
||||
|
||||
def test_orders_cancel_unpaid_fee(self):
|
||||
self.order.status = Order.STATUS_PENDING
|
||||
self.order.save()
|
||||
with scopes_disabled():
|
||||
self.order.payments.create(provider='testdummy_partialrefund', amount=self.order.total, state=OrderPayment.PAYMENT_STATE_CREATED)
|
||||
self.event.settings.cancel_allow_user = True
|
||||
self.event.settings.cancel_allow_user_paid = False
|
||||
self.event.settings.cancel_allow_user_unpaid_keep = Decimal('3.00')
|
||||
self.event.settings.cancel_allow_user_paid_keep = Decimal('7.00')
|
||||
response = self.client.get(
|
||||
'/%s/%s/order/%s/%s/cancel' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert 'manually' not in response.content.decode()
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/cancel/do' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), {
|
||||
}, follow=True)
|
||||
self.assertRedirects(response,
|
||||
'/%s/%s/order/%s/%s/' % (self.orga.slug, self.event.slug, self.order.code,
|
||||
self.order.secret),
|
||||
target_status_code=200)
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.status == Order.STATUS_PENDING
|
||||
assert self.order.total == Decimal('3.00')
|
||||
with scopes_disabled():
|
||||
assert self.order.refunds.count() == 0
|
||||
|
||||
def test_orders_cancel_paid_fee_autorefund(self):
|
||||
self.order.status = Order.STATUS_PAID
|
||||
self.order.save()
|
||||
|
||||
Reference in New Issue
Block a user