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
|
refund_amount = o.payment_refund_sum
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if auto_refund:
|
if auto_refund or manual_refund:
|
||||||
_try_auto_refund(o.pk, manual_refund=manual_refund, allow_partial=True,
|
_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,
|
source=OrderRefund.REFUND_SOURCE_ADMIN, refund_as_giftcard=refund_as_giftcard,
|
||||||
giftcard_expires=giftcard_expires, giftcard_conditions=giftcard_conditions,
|
giftcard_expires=giftcard_expires, giftcard_conditions=giftcard_conditions,
|
||||||
comment=gettext('Event canceled'))
|
comment=gettext('Event canceled'))
|
||||||
@@ -272,8 +272,8 @@ def cancel_event(self, event: Event, subevent: int, auto_refund: bool,
|
|||||||
ocm.commit()
|
ocm.commit()
|
||||||
refund_amount = o.payment_refund_sum - o.total
|
refund_amount = o.payment_refund_sum - o.total
|
||||||
|
|
||||||
if auto_refund:
|
if auto_refund or manual_refund:
|
||||||
_try_auto_refund(o.pk, manual_refund=manual_refund, allow_partial=True,
|
_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,
|
source=OrderRefund.REFUND_SOURCE_ADMIN, refund_as_giftcard=refund_as_giftcard,
|
||||||
giftcard_expires=giftcard_expires, giftcard_conditions=giftcard_conditions,
|
giftcard_expires=giftcard_expires, giftcard_conditions=giftcard_conditions,
|
||||||
comment=gettext('Event canceled'))
|
comment=gettext('Event canceled'))
|
||||||
|
|||||||
@@ -2385,7 +2385,8 @@ def perform_order(self, event: Event, payment_provider: str, positions: List[str
|
|||||||
_unset = object()
|
_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):
|
refund_as_giftcard=False, giftcard_expires=_unset, giftcard_conditions=None, comment=None):
|
||||||
notify_admin = False
|
notify_admin = False
|
||||||
error = 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'):
|
if refund_amount <= Decimal('0.00'):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
can_auto_refund_sum = 0
|
||||||
|
|
||||||
if refund_as_giftcard:
|
if refund_as_giftcard:
|
||||||
proposals = {}
|
|
||||||
can_auto_refund = True
|
|
||||||
can_auto_refund_sum = refund_amount
|
can_auto_refund_sum = refund_amount
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
giftcard = order.event.organizer.issued_gift_cards.create(
|
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:
|
if r.state != OrderRefund.REFUND_STATE_DONE:
|
||||||
notify_admin = True
|
notify_admin = True
|
||||||
|
|
||||||
else:
|
elif auto_refund:
|
||||||
proposals = order.propose_auto_refunds(refund_amount)
|
proposals = order.propose_auto_refunds(refund_amount)
|
||||||
can_auto_refund_sum = sum(proposals.values())
|
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 (allow_partial and can_auto_refund_sum) or can_auto_refund_sum == refund_amount:
|
||||||
if can_auto_refund:
|
for p, value in proposals.items():
|
||||||
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:
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
r.state = OrderRefund.REFUND_STATE_FAILED
|
r = order.refunds.create(
|
||||||
r.save()
|
payment=p,
|
||||||
order.log_action('pretix.event.order.refund.failed', {
|
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,
|
'local_id': r.local_id,
|
||||||
'provider': r.provider,
|
'provider': r.provider,
|
||||||
'error': str(e)
|
|
||||||
})
|
})
|
||||||
error = True
|
|
||||||
notify_admin = True
|
try:
|
||||||
else:
|
r.payment_provider.execute_refund(r)
|
||||||
if r.state not in (OrderRefund.REFUND_STATE_TRANSIT, OrderRefund.REFUND_STATE_DONE):
|
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
|
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 refund_amount - can_auto_refund_sum > Decimal('0.00'):
|
||||||
if manual_refund:
|
if manual_refund:
|
||||||
|
|||||||
@@ -749,16 +749,17 @@ class EventCancelForm(forms.Form):
|
|||||||
auto_refund = forms.BooleanField(
|
auto_refund = forms.BooleanField(
|
||||||
label=_('Automatically refund money if possible'),
|
label=_('Automatically refund money if possible'),
|
||||||
initial=True,
|
initial=True,
|
||||||
required=False
|
required=False,
|
||||||
|
help_text=_('Only available for payment method that support automatic refunds.')
|
||||||
)
|
)
|
||||||
manual_refund = forms.BooleanField(
|
manual_refund = forms.BooleanField(
|
||||||
label=_('Create manual refund if the payment method does not support automatic refunds'),
|
label=_('Create refund in the manual refund to-do list'),
|
||||||
widget=forms.CheckboxInput(attrs={'data-display-dependency': '#id_auto_refund'}),
|
|
||||||
initial=True,
|
initial=True,
|
||||||
required=False,
|
required=False,
|
||||||
help_text=_('If checked, all payments with a payment method not supporting automatic refunds will be on your '
|
help_text=_('Manual refunds will be created which will be listed in the manual refund to-do list. '
|
||||||
'manual refund to-do list. Do not check if you want to refund some of the orders by offsetting '
|
'When combined with the automatic refund functionally, only payments with a payment method not '
|
||||||
'with different orders or issuing gift cards.')
|
'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(
|
refund_as_giftcard = forms.BooleanField(
|
||||||
label=_('Refund order value to a gift card instead instead of the original payment method'),
|
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.source == OrderRefund.REFUND_SOURCE_ADMIN
|
||||||
assert r.payment == p1
|
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):
|
class SubEventCancelTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user