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() @transaction.atomic()
def execute_refund(self, refund: OrderRefund): def execute_refund(self, refund: OrderRefund):
from .models import GiftCard 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( trans = gc.transactions.create(
value=refund.amount, value=refund.amount,
order=refund.order, order=refund.order,

View File

@@ -83,6 +83,27 @@
value="" title="" class="form-control"> value="" title="" class="form-control">
</td> </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>
<tr> <tr>
<td></td> <td></td>

View File

@@ -5,6 +5,7 @@ import os
import re import re
from datetime import datetime, time, timedelta from datetime import datetime, time, timedelta
from decimal import Decimal, DecimalException from decimal import Decimal, DecimalException
from urllib.parse import urlencode
import vat_moss.id import vat_moss.id
from django.conf import settings from django.conf import settings
@@ -709,6 +710,33 @@ class OrderRefundView(OrderView):
provider='manual' 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 = self.request.POST.get('refund-offsetting', '0') or '0'
offsetting_value = formats.sanitize_separators(offsetting_value) offsetting_value = formats.sanitize_separators(offsetting_value)
try: try:
@@ -779,7 +807,7 @@ class OrderRefundView(OrderView):
'local_id': r.local_id, 'local_id': r.local_id,
'provider': r.provider, 'provider': r.provider,
}, user=self.request.user) }, user=self.request.user)
if r.payment or r.provider == "offsetting": if r.payment or r.provider == "offsetting" or r.provider == "giftcard":
try: try:
r.payment_provider.execute_refund(r) r.payment_provider.execute_refund(r)
except PaymentException as e: except PaymentException as e:
@@ -816,6 +844,23 @@ class OrderRefundView(OrderView):
) )
self.order.save(update_fields=['status', 'expires']) 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()) return redirect(self.get_order_url())
else: else:
messages.error(self.request, _('The refunds you selected do not match the selected total refund ' 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, event=self.request.event,
code=self.kwargs['code'].upper() 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 return kwargs
def form_invalid(self, form): def form_invalid(self, form):

View File

@@ -932,7 +932,7 @@ class GiftCardListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
def get_queryset(self): def get_queryset(self):
qs = self.request.organizer.issued_gift_cards.annotate( 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(): if self.filter_form.is_valid():
qs = self.filter_form.filter_qs(qs) 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 tests.plugins.stripe.test_provider import MockedCharge
from pretix.base.models import ( from pretix.base.models import (
Event, InvoiceAddress, Item, Order, OrderFee, OrderPayment, OrderPosition, Event, GiftCard, InvoiceAddress, Item, Order, OrderFee, OrderPayment,
OrderRefund, Organizer, Question, QuestionAnswer, Quota, Team, User, OrderPosition, OrderRefund, Organizer, Question, QuestionAnswer, Quota,
Team, User,
) )
from pretix.base.payment import PaymentException from pretix.base.payment import PaymentException
from pretix.base.services.invoices import ( 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 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 @pytest.mark.django_db
def test_refund_list(client, env): def test_refund_list(client, env):
with scopes_disabled(): with scopes_disabled():