forked from CGM_Public/pretix_original
Event cancellation: Allow allow creating manual refunds for all orders (#2526)
This commit is contained in:
@@ -214,8 +214,8 @@ def cancel_event(self, event: Event, subevent: int, auto_refund: bool,
|
||||
refund_amount = o.payment_refund_sum
|
||||
|
||||
try:
|
||||
if auto_refund:
|
||||
_try_auto_refund(o.pk, manual_refund=manual_refund, allow_partial=True,
|
||||
if auto_refund or manual_refund:
|
||||
_try_auto_refund(o.pk, auto_refund=auto_refund, manual_refund=manual_refund, allow_partial=True,
|
||||
source=OrderRefund.REFUND_SOURCE_ADMIN, refund_as_giftcard=refund_as_giftcard,
|
||||
giftcard_expires=giftcard_expires, giftcard_conditions=giftcard_conditions,
|
||||
comment=gettext('Event canceled'))
|
||||
@@ -272,8 +272,8 @@ def cancel_event(self, event: Event, subevent: int, auto_refund: bool,
|
||||
ocm.commit()
|
||||
refund_amount = o.payment_refund_sum - o.total
|
||||
|
||||
if auto_refund:
|
||||
_try_auto_refund(o.pk, manual_refund=manual_refund, allow_partial=True,
|
||||
if auto_refund or manual_refund:
|
||||
_try_auto_refund(o.pk, auto_refund=auto_refund, manual_refund=manual_refund, allow_partial=True,
|
||||
source=OrderRefund.REFUND_SOURCE_ADMIN, refund_as_giftcard=refund_as_giftcard,
|
||||
giftcard_expires=giftcard_expires, giftcard_conditions=giftcard_conditions,
|
||||
comment=gettext('Event canceled'))
|
||||
|
||||
@@ -2385,7 +2385,8 @@ def perform_order(self, event: Event, payment_provider: str, positions: List[str
|
||||
_unset = object()
|
||||
|
||||
|
||||
def _try_auto_refund(order, manual_refund=False, allow_partial=False, source=OrderRefund.REFUND_SOURCE_BUYER,
|
||||
def _try_auto_refund(order, auto_refund=True, manual_refund=False, allow_partial=False,
|
||||
source=OrderRefund.REFUND_SOURCE_BUYER,
|
||||
refund_as_giftcard=False, giftcard_expires=_unset, giftcard_conditions=None, comment=None):
|
||||
notify_admin = False
|
||||
error = False
|
||||
@@ -2395,9 +2396,9 @@ def _try_auto_refund(order, manual_refund=False, allow_partial=False, source=Ord
|
||||
if refund_amount <= Decimal('0.00'):
|
||||
return
|
||||
|
||||
can_auto_refund_sum = 0
|
||||
|
||||
if refund_as_giftcard:
|
||||
proposals = {}
|
||||
can_auto_refund = True
|
||||
can_auto_refund_sum = refund_amount
|
||||
with transaction.atomic():
|
||||
giftcard = order.event.organizer.issued_gift_cards.create(
|
||||
@@ -2437,42 +2438,41 @@ def _try_auto_refund(order, manual_refund=False, allow_partial=False, source=Ord
|
||||
if r.state != OrderRefund.REFUND_STATE_DONE:
|
||||
notify_admin = True
|
||||
|
||||
else:
|
||||
elif auto_refund:
|
||||
proposals = order.propose_auto_refunds(refund_amount)
|
||||
can_auto_refund_sum = sum(proposals.values())
|
||||
can_auto_refund = (allow_partial and can_auto_refund_sum) or can_auto_refund_sum == refund_amount
|
||||
if can_auto_refund:
|
||||
for p, value in proposals.items():
|
||||
with transaction.atomic():
|
||||
r = order.refunds.create(
|
||||
payment=p,
|
||||
source=source,
|
||||
state=OrderRefund.REFUND_STATE_CREATED,
|
||||
amount=value,
|
||||
comment=comment,
|
||||
provider=p.provider
|
||||
)
|
||||
order.log_action('pretix.event.order.refund.created', {
|
||||
'local_id': r.local_id,
|
||||
'provider': r.provider,
|
||||
})
|
||||
|
||||
try:
|
||||
r.payment_provider.execute_refund(r)
|
||||
except PaymentException as e:
|
||||
if (allow_partial and can_auto_refund_sum) or can_auto_refund_sum == refund_amount:
|
||||
for p, value in proposals.items():
|
||||
with transaction.atomic():
|
||||
r.state = OrderRefund.REFUND_STATE_FAILED
|
||||
r.save()
|
||||
order.log_action('pretix.event.order.refund.failed', {
|
||||
r = order.refunds.create(
|
||||
payment=p,
|
||||
source=source,
|
||||
state=OrderRefund.REFUND_STATE_CREATED,
|
||||
amount=value,
|
||||
comment=comment,
|
||||
provider=p.provider
|
||||
)
|
||||
order.log_action('pretix.event.order.refund.created', {
|
||||
'local_id': r.local_id,
|
||||
'provider': r.provider,
|
||||
'error': str(e)
|
||||
})
|
||||
error = True
|
||||
notify_admin = True
|
||||
else:
|
||||
if r.state not in (OrderRefund.REFUND_STATE_TRANSIT, OrderRefund.REFUND_STATE_DONE):
|
||||
|
||||
try:
|
||||
r.payment_provider.execute_refund(r)
|
||||
except PaymentException as e:
|
||||
with transaction.atomic():
|
||||
r.state = OrderRefund.REFUND_STATE_FAILED
|
||||
r.save()
|
||||
order.log_action('pretix.event.order.refund.failed', {
|
||||
'local_id': r.local_id,
|
||||
'provider': r.provider,
|
||||
'error': str(e)
|
||||
})
|
||||
error = True
|
||||
notify_admin = True
|
||||
else:
|
||||
if r.state not in (OrderRefund.REFUND_STATE_TRANSIT, OrderRefund.REFUND_STATE_DONE):
|
||||
notify_admin = True
|
||||
|
||||
if refund_amount - can_auto_refund_sum > Decimal('0.00'):
|
||||
if manual_refund:
|
||||
|
||||
@@ -749,16 +749,17 @@ class EventCancelForm(forms.Form):
|
||||
auto_refund = forms.BooleanField(
|
||||
label=_('Automatically refund money if possible'),
|
||||
initial=True,
|
||||
required=False
|
||||
required=False,
|
||||
help_text=_('Only available for payment method that support automatic refunds.')
|
||||
)
|
||||
manual_refund = forms.BooleanField(
|
||||
label=_('Create manual refund if the payment method does not support automatic refunds'),
|
||||
widget=forms.CheckboxInput(attrs={'data-display-dependency': '#id_auto_refund'}),
|
||||
label=_('Create refund in the manual refund to-do list'),
|
||||
initial=True,
|
||||
required=False,
|
||||
help_text=_('If checked, all payments with a payment method not supporting automatic refunds will be on your '
|
||||
'manual refund to-do list. Do not check if you want to refund some of the orders by offsetting '
|
||||
'with different orders or issuing gift cards.')
|
||||
help_text=_('Manual refunds will be created which will be listed in the manual refund to-do list. '
|
||||
'When combined with the automatic refund functionally, only payments with a payment method not '
|
||||
'supporting automatic refunds will be on your manual refund to-do list. Do not check if you want '
|
||||
'to refund some of the orders by offsetting with different orders or issuing gift cards.')
|
||||
)
|
||||
refund_as_giftcard = forms.BooleanField(
|
||||
label=_('Refund order value to a gift card instead instead of the original payment method'),
|
||||
|
||||
@@ -413,6 +413,37 @@ class EventCancelTests(TestCase):
|
||||
assert r.source == OrderRefund.REFUND_SOURCE_ADMIN
|
||||
assert r.payment == p1
|
||||
|
||||
@classscope(attr='o')
|
||||
def test_cancel_refund_paid_only_manual(self):
|
||||
gc = self.o.issued_gift_cards.create(currency="EUR")
|
||||
self.order.payments.create(
|
||||
amount=Decimal('20.00'),
|
||||
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
||||
provider='giftcard',
|
||||
info='{"gift_card": %d}' % gc.pk
|
||||
)
|
||||
self.order.payments.create(
|
||||
amount=Decimal('26.00'),
|
||||
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
||||
provider='manual',
|
||||
)
|
||||
self.order.status = Order.STATUS_PAID
|
||||
self.order.save()
|
||||
|
||||
cancel_event(
|
||||
self.event.pk, subevent=None, manual_refund=True,
|
||||
auto_refund=False, keep_fee_fixed="0.00", keep_fee_percentage="0.00", keep_fee_per_ticket="",
|
||||
send=False, send_subject="Event canceled", send_message="Event canceled :-(",
|
||||
user=None
|
||||
)
|
||||
|
||||
assert self.order.refunds.count() == 1
|
||||
r = self.order.refunds.get(provider='manual')
|
||||
assert r.state == OrderRefund.REFUND_STATE_CREATED
|
||||
assert r.amount == Decimal('46.00')
|
||||
assert r.source == OrderRefund.REFUND_SOURCE_ADMIN
|
||||
assert r.payment is None
|
||||
|
||||
|
||||
class SubEventCancelTests(TestCase):
|
||||
def setUp(self):
|
||||
|
||||
Reference in New Issue
Block a user