Allow to create a new gift card when refunding

This commit is contained in:
Raphael Michel
2019-11-28 10:13:39 +01:00
parent 46d4d97c13
commit 5462e256ac
5 changed files with 105 additions and 5 deletions

View File

@@ -1149,7 +1149,7 @@ class GiftCardPayment(BasePaymentProvider):
@transaction.atomic()
def execute_refund(self, refund: OrderRefund):
from .models import GiftCard
gc = GiftCard.objects.get(pk=refund.payment.info_data.get('gift_card'))
gc = GiftCard.objects.get(pk=refund.info_data.get('gift_card') or refund.payment.info_data.get('gift_card'))
trans = gc.transactions.create(
value=refund.amount,
order=refund.order,

View File

@@ -83,6 +83,27 @@
value="" title="" class="form-control">
</td>
</tr>
<tr>
<td></td>
<td></td>
<td>
<strong>{% trans "Create a new gift card" %}</strong>
</td>
<td></td>
<td>
<div class="input-group">
<input type="text" name="refund-new-giftcard"
title="" class="form-control" value="{{ 0|floatformat:2 }}">
<span class="input-group-addon">
{{ request.event.currency }}
</span>
</div>
<div class="text-muted">
{% trans "The gift card can be used to buy tickets for all events of this organizer." %}
</div>
</td>
</tr>
<tr>
<td></td>

View File

@@ -5,6 +5,7 @@ import os
import re
from datetime import datetime, time, timedelta
from decimal import Decimal, DecimalException
from urllib.parse import urlencode
import vat_moss.id
from django.conf import settings
@@ -709,6 +710,33 @@ class OrderRefundView(OrderView):
provider='manual'
))
giftcard_value = self.request.POST.get('refund-new-giftcard', '0') or '0'
giftcard_value = formats.sanitize_separators(giftcard_value)
try:
giftcard_value = Decimal(giftcard_value)
except (DecimalException, TypeError):
messages.error(self.request, _('You entered an invalid number.'))
is_valid = False
else:
if giftcard_value:
refund_selected += giftcard_value
giftcard = self.request.organizer.issued_gift_cards.create(
currency=self.request.event.currency,
testmode=self.order.testmode
)
refunds.append(OrderRefund(
order=self.order,
payment=None,
source=OrderRefund.REFUND_SOURCE_ADMIN,
state=OrderRefund.REFUND_STATE_CREATED,
execution_date=now(),
amount=giftcard_value,
provider='giftcard',
info=json.dumps({
'gift_card': giftcard.pk
})
))
offsetting_value = self.request.POST.get('refund-offsetting', '0') or '0'
offsetting_value = formats.sanitize_separators(offsetting_value)
try:
@@ -779,7 +807,7 @@ class OrderRefundView(OrderView):
'local_id': r.local_id,
'provider': r.provider,
}, user=self.request.user)
if r.payment or r.provider == "offsetting":
if r.payment or r.provider == "offsetting" or r.provider == "giftcard":
try:
r.payment_provider.execute_refund(r)
except PaymentException as e:
@@ -816,6 +844,23 @@ class OrderRefundView(OrderView):
)
self.order.save(update_fields=['status', 'expires'])
if giftcard_value and self.order.email:
messages.success(self.request, _('A new gift card was created. You can now send the user their '
'gift card code.'))
return redirect(reverse('control:event.order.sendmail', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
'code': self.order.code
}) + '?' + urlencode({
'subject': _('Your gift card code'),
'message': _('Hello,\n\nwe have refunded you {amount} for your order.\n\nYou can use the gift '
'card code {giftcard} to pay for future ticket purchases in our shop.\n\n'
'Your {event} team').format(
event="{event}",
amount=money_filter(giftcard_value, self.request.event.currency),
giftcard=giftcard.secret,
)
}))
return redirect(self.get_order_url())
else:
messages.error(self.request, _('The refunds you selected do not match the selected total refund '
@@ -1563,6 +1608,11 @@ class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView):
event=self.request.event,
code=self.kwargs['code'].upper()
)
kwargs['initial'] = {}
if self.request.GET.get('subject'):
kwargs['initial']['subject'] = self.request.GET.get('subject')
if self.request.GET.get('message'):
kwargs['initial']['message'] = self.request.GET.get('message')
return kwargs
def form_invalid(self, form):

View File

@@ -932,7 +932,7 @@ class GiftCardListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
def get_queryset(self):
qs = self.request.organizer.issued_gift_cards.annotate(
cached_value=Sum('transactions__value')
cached_value=Coalesce(Sum('transactions__value'), Decimal('0.00'))
)
if self.filter_form.is_valid():
qs = self.filter_form.filter_qs(qs)

View File

@@ -12,8 +12,9 @@ from tests.base import SoupTest
from tests.plugins.stripe.test_provider import MockedCharge
from pretix.base.models import (
Event, InvoiceAddress, Item, Order, OrderFee, OrderPayment, OrderPosition,
OrderRefund, Organizer, Question, QuestionAnswer, Quota, Team, User,
Event, GiftCard, InvoiceAddress, Item, Order, OrderFee, OrderPayment,
OrderPosition, OrderRefund, Organizer, Question, QuestionAnswer, Quota,
Team, User,
)
from pretix.base.payment import PaymentException
from pretix.base.services.invoices import (
@@ -2046,6 +2047,34 @@ def test_refund_paid_order_offsetting(client, env):
assert p2.state == OrderPayment.PAYMENT_STATE_CONFIRMED
@pytest.mark.django_db
def test_refund_paid_order_giftcard(client, env):
with scopes_disabled():
p = env[2].payments.last()
p.confirm()
client.login(email='dummy@dummy.dummy', password='dummy')
client.post('/control/event/dummy/dummy/orders/FOO/refund', {
'start-partial_amount': '5.00',
'start-mode': 'partial',
'start-action': 'mark_pending',
'refund-new-giftcard': '5.00',
'manual_state': 'pending',
'perform': 'on'
}, follow=True)
p.refresh_from_db()
assert p.state == OrderPayment.PAYMENT_STATE_CONFIRMED
env[2].refresh_from_db()
with scopes_disabled():
r = env[2].refunds.last()
assert r.provider == "giftcard"
assert r.state == OrderRefund.REFUND_STATE_DONE
assert r.amount == Decimal('5.00')
assert env[2].status == Order.STATUS_PENDING
gk = GiftCard.objects.get(pk=r.info_data['gift_card'])
assert gk.value == Decimal('5.00')
@pytest.mark.django_db
def test_refund_list(client, env):
with scopes_disabled():