forked from CGM_Public/pretix_original
Allow to create a new gift card when refunding
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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():
|
||||||
|
|||||||
Reference in New Issue
Block a user