diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index 18c642c231..04e7a5d73e 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -210,6 +210,8 @@ def build_cancellation(invoice: Invoice): def generate_cancellation(invoice: Invoice, trigger_pdf=True): + if invoice.refered.exists(): + raise ValueError("Invoice should not be canceled twice.") cancellation = modelcopy(invoice) cancellation.pk = None cancellation.invoice_no = None diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index ca0c4f2ccc..d3160d7001 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -156,7 +156,7 @@ def mark_order_expired(order, user=None, auth=None): order.log_action('pretix.event.order.expired', user=user, auth=auth) i = order.invoices.filter(is_cancellation=False).last() - if i: + if i and not i.refered.exists(): generate_cancellation(i) order_expired.send(order.event, order=order) diff --git a/src/tests/base/test_orders.py b/src/tests/base/test_orders.py index 412fa137b7..28b784c434 100644 --- a/src/tests/base/test_orders.py +++ b/src/tests/base/test_orders.py @@ -213,6 +213,28 @@ def test_expiring_paid_invoice(event): assert o2.invoices.last().is_cancellation is False +@pytest.mark.django_db +def test_expire_twice(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, + ) + generate_invoice(o2) + expire_orders(None) + o2 = Order.objects.get(id=o2.id) + assert o2.status == Order.STATUS_EXPIRED + assert o2.invoices.count() == 2 + # this would usually happen when the deadline is extended, but lets keep the case simple + o2.status = Order.STATUS_PENDING + o2.save() + expire_orders(None) + o2 = Order.objects.get(id=o2.id) + assert o2.status == Order.STATUS_EXPIRED + assert o2.invoices.count() == 2 + + @pytest.mark.django_db def test_expiring_auto_disabled(event): event.settings.set('payment_term_expire_automatically', False) diff --git a/src/tests/control/test_orders.py b/src/tests/control/test_orders.py index 5b2e4381d1..cc606440d3 100644 --- a/src/tests/control/test_orders.py +++ b/src/tests/control/test_orders.py @@ -591,6 +591,8 @@ def test_order_extend_not_expired(client, env): with scopes_disabled(): q = Quota.objects.create(event=env[0], size=0) q.items.add(env[3]) + o = Order.objects.get(id=env[2].id) + generate_invoice(o) newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d %H:%M:%S") client.login(email='dummy@dummy.dummy', password='dummy') response = client.post('/control/event/dummy/dummy/orders/FOO/extend', { @@ -600,6 +602,7 @@ def test_order_extend_not_expired(client, env): with scopes_disabled(): o = Order.objects.get(id=env[2].id) assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59" + assert o.invoices.count() == 1 @pytest.mark.django_db @@ -631,6 +634,7 @@ def test_order_extend_overdue_quota_blocked_by_waiting_list(client, env): q = Quota.objects.create(event=env[0], size=1) q.items.add(env[3]) env[0].waitinglistentries.create(item=env[3], email='foo@bar.com') + generate_cancellation(generate_invoice(o)) newdate = (now() + timedelta(days=20)).strftime("%Y-%m-%d %H:%M:%S") client.login(email='dummy@dummy.dummy', password='dummy') @@ -642,6 +646,7 @@ def test_order_extend_overdue_quota_blocked_by_waiting_list(client, env): o = Order.objects.get(id=env[2].id) assert o.expires.strftime("%Y-%m-%d %H:%M:%S") == newdate[:10] + " 23:59:59" assert o.status == Order.STATUS_PENDING + assert o.invoices.count() == 3 @pytest.mark.django_db