diff --git a/src/pretix/base/migrations/0302_resolve_duplicate_codes_and_secrets.py b/src/pretix/base/migrations/0302_resolve_duplicate_codes_and_secrets.py new file mode 100644 index 0000000000..01b1cfdebb --- /dev/null +++ b/src/pretix/base/migrations/0302_resolve_duplicate_codes_and_secrets.py @@ -0,0 +1,58 @@ +# Generated by Django 4.2.8 on 2024-07-01 09:27 +import logging + +from django.db import migrations +from django.db.models import Count + +logger = logging.getLogger(__name__) + + +def clean_duplicate_secrets(apps, schema_editor): + # This will autofix all possible duplicate Order.code and OrderPosition.secret values, + # unless Order.code is already too long to append something. This would need to be fixed by + # sysadmins manually. + OrderPosition = apps.get_model("pretixbase", "OrderPosition") + Order = apps.get_model("pretixbase", "Order") + + qs = OrderPosition.all.values("secret", "order__event__organizer_id").order_by().annotate(c=Count("*")).filter(c__gt=1) + for row in qs: + affected = OrderPosition.all.filter( + **{k: v for k, v in row.items() if k != "c"} + ).order_by("pk") + logger.error(f"Found {row['c']} tickets with with the same secret \"{row["secret"]}\" in organizer {row['order__event__organizer_id']}, all except one will be changed") + for i, a in enumerate(affected): + if i > 0: + a.secret = a.secret + "__dupl__" + str(a.pk) + logger.info( + f"Ticket {a.pk} has new secret {a.secret}" + ) + a.save(update_fields=["organizer_id", "secret"]) + + qs = Order.objects.values("code", "event__organizer_id").order_by().annotate(c=Count("*")).filter(c__gt=1) + for row in qs: + affected = Order.objects.filter( + **{k: v for k, v in row.items() if k != "c"} + ).order_by("pk") + logger.error(f"Found {row['c']} orders with with the same code \"{row["code"]}\" in organizer {row['event__organizer_id']}, all except one will be changed") + for i, a in enumerate(affected): + if i > 0: + if len(a.code) > 16 - len(str(a.pk)): + raise ValueError(f"Cannot auto-fix order with duplicate code {a.code}, order code is too long already") + a.code = a.code + str(a.pk).zfill(16 - len(a.code)) + logger.info( + f"Order {a.pk} has new code {a.code}" + ) + a.save(update_fields=["organizer_id", "code"]) + + +class Migration(migrations.Migration): + dependencies = [ + ( + "pretixbase", + "0301_reusablemedium_remove_orderposition", + ), + ] + + operations = [ + migrations.RunPython(clean_duplicate_secrets, migrations.RunPython.noop), + ] diff --git a/src/pretix/base/migrations/0269_alter_order_organizer_alter_orderposition_organizer.py b/src/pretix/base/migrations/0303_alter_order_organizer_alter_orderposition_organizer.py similarity index 68% rename from src/pretix/base/migrations/0269_alter_order_organizer_alter_orderposition_organizer.py rename to src/pretix/base/migrations/0303_alter_order_organizer_alter_orderposition_organizer.py index a96ee2de93..237c5e7a52 100644 --- a/src/pretix/base/migrations/0269_alter_order_organizer_alter_orderposition_organizer.py +++ b/src/pretix/base/migrations/0303_alter_order_organizer_alter_orderposition_organizer.py @@ -1,15 +1,14 @@ # Generated by Django 4.2.8 on 2024-07-01 09:27 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ( "pretixbase", - "0268_remove_subevent_items_remove_subevent_variations_and_more", + "0302_resolve_duplicate_codes_and_secrets", ), ] @@ -17,21 +16,12 @@ class Migration(migrations.Migration): migrations.RunSQL( "UPDATE pretixbase_order " "SET organizer_id = (SELECT e.organizer_id FROM pretixbase_event e WHERE e.id = pretixbase_order.event_id) " - "WHERE pretixbase_order.id IN (SELECT id FROM pretixbase_order o2 WHERE o2.organizer_id IS NULL);" + "WHERE pretixbase_order.organizer_id IS NULL;" ), migrations.RunSQL( "UPDATE pretixbase_orderposition " "SET organizer_id = (SELECT e.organizer_id FROM pretixbase_order o LEFT JOIN pretixbase_event e ON e.id = o.event_id WHERE o.id = pretixbase_orderposition.order_id) " - "WHERE pretixbase_orderposition.id IN (SELECT id FROM pretixbase_orderposition op2 WHERE op2.organizer_id IS NULL);" - ), - migrations.AlterField( - model_name="order", - name="organizer", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="orders", - to="pretixbase.organizer", - ), + "WHERE pretixbase_orderposition.organizer_id IS NULL;" ), migrations.AlterField( model_name="order",