From cbc9231ab9eadf446dcbe5c447fdd7e0b1d31294 Mon Sep 17 00:00:00 2001 From: Mira Weller Date: Fri, 7 Aug 2026 20:17:17 +0200 Subject: [PATCH] Add CheckoutSession model --- ...session_invoiceaddress_checkout_session.py | 62 +++++++++++++++++++ src/pretix/base/models/orders.py | 40 ++++++++++++ src/pretix/base/services/cleanup.py | 5 ++ 3 files changed, 107 insertions(+) create mode 100644 src/pretix/base/migrations/0308_checkoutsession_invoiceaddress_checkout_session.py diff --git a/src/pretix/base/migrations/0308_checkoutsession_invoiceaddress_checkout_session.py b/src/pretix/base/migrations/0308_checkoutsession_invoiceaddress_checkout_session.py new file mode 100644 index 0000000000..b1230d4376 --- /dev/null +++ b/src/pretix/base/migrations/0308_checkoutsession_invoiceaddress_checkout_session.py @@ -0,0 +1,62 @@ +# Generated by Django 4.2.17 on 2025-01-01 20:25 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("pretixbase", "0307_devicelastseen"), + ] + + operations = [ + migrations.CreateModel( + name="CheckoutSession", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False + ), + ), + ("cart_id", models.CharField(max_length=255, unique=True)), + ("created", models.DateTimeField(auto_now_add=True)), + ("testmode", models.BooleanField(default=False)), + ("session_data", models.JSONField(default=dict)), + ( + "customer", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="checkout_sessions", + to="pretixbase.customer", + ), + ), + ( + "event", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="pretixbase.event", + ), + ), + ( + "sales_channel", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="pretixbase.saleschannel", + ), + ), + ], + ), + migrations.AddField( + model_name="invoiceaddress", + name="checkout_session", + field=models.OneToOneField( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="invoice_address", + to="pretixbase.checkoutsession", + ), + ), + ] diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 01844c82f8..8b80fa4a8f 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -3177,6 +3177,39 @@ class Transaction(models.Model): return self.tax_value_includes_rounding_correction * self.count +class CheckoutSession(models.Model): + """ + A checkout session optionally bundles cart positions with additional information. This is historically + not required in pretix and currently only used in the Storefront API. + """ + event = models.ForeignKey( + Event, + verbose_name=_("Event"), + related_name="checkout_sessions", + on_delete=models.CASCADE, + ) + cart_id = models.CharField( + max_length=255, unique=True, + verbose_name=_("Cart ID (e.g. session key)"), + ) + created = models.DateTimeField( + verbose_name=_("Date"), + auto_now_add=True, + ) + customer = models.ForeignKey( + Customer, + related_name='checkout_sessions', + null=True, blank=True, + on_delete=models.SET_NULL, + ) + sales_channel = models.ForeignKey( + "SalesChannel", + on_delete=models.CASCADE, + ) + testmode = models.BooleanField(default=False) + session_data = models.JSONField(default=dict) + + class CartPosition(AbstractPosition): """ A cart position is similar to an order line, except that it is not @@ -3381,6 +3414,13 @@ class CartPosition(AbstractPosition): class InvoiceAddress(models.Model): last_modified = models.DateTimeField(auto_now=True) + checkout_session = models.OneToOneField( + CheckoutSession, + null=True, + blank=True, + related_name='invoice_address', + on_delete=models.CASCADE + ) order = models.OneToOneField(Order, null=True, blank=True, related_name='invoice_address', on_delete=models.CASCADE) customer = models.ForeignKey( Customer, diff --git a/src/pretix/base/services/cleanup.py b/src/pretix/base/services/cleanup.py index 41376b920f..b6f4715b0e 100644 --- a/src/pretix/base/services/cleanup.py +++ b/src/pretix/base/services/cleanup.py @@ -33,6 +33,7 @@ from pretix.base.models.customers import CustomerSSOGrant from ..models import CachedFile, CartPosition, InvoiceAddress from ..models.auth import UserKnownLoginSource +from ..models.orders import CheckoutSession from ..signals import periodic_task @@ -43,6 +44,10 @@ def clean_cart_positions(sender, **kwargs): cp.delete() for cp in CartPosition.objects.filter(expires__lt=now() - timedelta(days=14), addon_to__isnull=True): cp.delete() + for cs in CheckoutSession.objects.filter(created__lt=now() - timedelta(days=14)).exclude( + Exists(CartPosition.objects.filter(cart_id=OuterRef("cart_id"))) + ): + cs.delete() for ia in InvoiceAddress.objects.filter(order__isnull=True, customer__isnull=True, last_modified__lt=now() - timedelta(days=14)): ia.delete()