Fix #400 -- Automatically create cancellations for invoices on expiry (#760)

This commit is contained in:
Raphael Michel
2018-01-26 09:09:04 +01:00
committed by GitHub
parent 67121decbf
commit 083c94403b
4 changed files with 68 additions and 19 deletions

View File

@@ -21,7 +21,8 @@ from pretix.base.models.organizer import TeamAPIToken
from pretix.base.services.invoices import invoice_pdf
from pretix.base.services.mail import SendMailException
from pretix.base.services.orders import (
OrderError, cancel_order, extend_order, mark_order_paid,
OrderError, cancel_order, extend_order, mark_order_expired,
mark_order_paid,
)
from pretix.base.services.tickets import (
get_cachedticket_for_order, get_cachedticket_for_position,
@@ -153,10 +154,8 @@ class OrderViewSet(viewsets.ReadOnlyModelViewSet):
status=status.HTTP_400_BAD_REQUEST
)
order.status = Order.STATUS_EXPIRED
order.save()
order.log_action(
'pretix.event.order.expired',
mark_order_expired(
order,
user=request.user if request.user.is_authenticated else None,
api_token=(request.auth if isinstance(request.auth, TeamAPIToken) else None),
)

View File

@@ -128,8 +128,14 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
order_paid.send(order.event, order=order)
invoice = None
if order.event.settings.get('invoice_generate') in ('True', 'paid') and invoice_qualified(order):
if not order.invoices.exists():
if invoice_qualified(order):
invoices = order.invoices.filter(is_cancellation=False).count()
cancellations = order.invoices.filter(is_cancellation=True).count()
gen_invoice = (
(invoices == 0 and order.event.settings.get('invoice_generate') in ('True', 'paid')) or
0 < invoices <= cancellations
)
if gen_invoice:
invoice = generate_invoice(
order,
trigger_pdf=not send_mail or not order.event.settings.invoice_email_attachment
@@ -231,6 +237,32 @@ def mark_order_refunded(order, user=None):
return order
@transaction.atomic
def mark_order_expired(order, user=None, api_token=None):
"""
Mark this order as expired. This sets the payment status and returns the order object.
:param order: The order to change
:param user: The user that performed the change
:param api_token: The API token used to performed the change
"""
if isinstance(order, int):
order = Order.objects.get(pk=order)
if isinstance(user, int):
user = User.objects.get(pk=user)
if isinstance(api_token, int):
api_token = TeamAPIToken.objects.get(pk=api_token)
with order.event.lock():
order.status = Order.STATUS_EXPIRED
order.save()
order.log_action('pretix.event.order.expired', user=user, api_token=api_token)
i = order.invoices.filter(is_cancellation=False).last()
if i:
generate_cancellation(i)
return order
@transaction.atomic
def _cancel_order(order, user=None, send_mail: bool=True, api_token=None):
"""
@@ -562,9 +594,7 @@ def expire_orders(sender, **kwargs):
expire = o.event.settings.get('payment_term_expire_automatically', as_type=bool)
eventcache[o.event.pk] = expire
if expire:
o.status = Order.STATUS_EXPIRED
o.log_action('pretix.event.order.expired')
o.save()
mark_order_expired(o)
@receiver(signal=periodic_task)

View File

@@ -38,7 +38,7 @@ from pretix.base.services.locking import LockTimeoutException
from pretix.base.services.mail import SendMailException, render_mail
from pretix.base.services.orders import (
OrderChangeManager, OrderError, cancel_order, extend_order,
mark_order_paid,
mark_order_expired, mark_order_paid,
)
from pretix.base.services.stats import order_overview
from pretix.base.signals import register_data_exporters
@@ -229,9 +229,7 @@ class OrderTransition(OrderView):
self.order.log_action('pretix.event.order.unpaid', user=self.request.user)
messages.success(self.request, _('The order has been marked as not paid.'))
elif self.order.status == Order.STATUS_PENDING and to == 'e':
self.order.status = Order.STATUS_EXPIRED
self.order.save()
self.order.log_action('pretix.event.order.expired', user=self.request.user)
mark_order_expired(self.order, user=self.request.user)
messages.success(self.request, _('The order has been marked as expired.'))
elif self.order.status == Order.STATUS_PAID and to == 'r':
ret = self.payment_provider.order_control_refund_perform(self.request, self.order)

View File

@@ -19,7 +19,7 @@ from pretix.base.reldate import RelativeDate, RelativeDateWrapper
from pretix.base.services.invoices import generate_invoice
from pretix.base.services.orders import (
OrderChangeManager, OrderError, _create_order, expire_orders,
send_download_reminders,
mark_order_paid, send_download_reminders,
)
@@ -28,7 +28,8 @@ def event():
o = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=now()
date_from=now(),
plugins='pretix.plugins.banktransfer'
)
return event
@@ -144,21 +145,42 @@ def test_expiry_dst(event):
def test_expiring(event):
o1 = Order.objects.create(
code='FOO', event=event, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
status=Order.STATUS_PENDING, locale='en',
datetime=now(), expires=now() + timedelta(days=10),
total=0, payment_provider='banktransfer'
)
o2 = Order.objects.create(
code='FO2', event=event, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
status=Order.STATUS_PENDING, locale='en',
datetime=now(), expires=now() - timedelta(days=10),
total=0, payment_provider='banktransfer'
total=12, payment_provider='banktransfer'
)
generate_invoice(o2)
expire_orders(None)
o1 = Order.objects.get(id=o1.id)
assert o1.status == Order.STATUS_PENDING
o2 = Order.objects.get(id=o2.id)
assert o2.status == Order.STATUS_EXPIRED
assert o2.invoices.count() == 2
assert o2.invoices.last().is_cancellation is True
@pytest.mark.django_db
def test_expiring_paid_invoice(event):
o2 = Order.objects.create(
code='FO2', event=event, email='dummy@dummy.test',
status=Order.STATUS_PENDING, locale='en',
datetime=now(), expires=now() - timedelta(days=10),
total=12, payment_provider='banktransfer'
)
generate_invoice(o2)
expire_orders(None)
o2 = Order.objects.get(id=o2.id)
assert o2.status == Order.STATUS_EXPIRED
assert o2.invoices.count() == 2
mark_order_paid(o2)
assert o2.invoices.count() == 3
assert o2.invoices.last().is_cancellation is False
@pytest.mark.django_db