diff --git a/src/pretix/base/migrations/0311_fix_unshredded_invoices.py b/src/pretix/base/migrations/0311_fix_unshredded_invoices.py new file mode 100644 index 0000000000..818166df53 --- /dev/null +++ b/src/pretix/base/migrations/0311_fix_unshredded_invoices.py @@ -0,0 +1,49 @@ +# Generated by Django 5.2.17 on 2026-09-21 11:30 + +import django.db.models.deletion +from django.db import migrations, models + + +def fix_unshredded_invoices(apps, _): + Invoice = apps.get_model("pretixbase", "Invoice") + InvoiceLine = apps.get_model("pretixbase", "InvoiceLine") + ignore_fields = ( + # bool/int fields are not listed and skipped automatically + 'prefix', 'invoice_no', 'full_invoice_no', 'invoice_from', 'invoice_from_name', 'invoice_from_zipcode', + 'invoice_from_city', 'invoice_from_state', 'invoice_from_country', 'invoice_from_tax_id', + 'invoice_from_vat_id', 'locale', 'payment_provider_stamp', 'footer_text', 'foreign_currency_display', + 'foreign_currency_source', 'transmission_type', 'transmission_provider', 'transmission_status', + ) + + for i in Invoice.objects.filter(shredded=True): + for f in Invoice._meta.fields: + if f.name in ignore_fields: + continue + val = getattr(i, f.name, None) + if val and isinstance(val, str): + setattr(i, f.name, "█") + elif val and isinstance(val, list): # jsonfield + setattr(i, f.name, []) + elif val and isinstance(val, dict): # jsonfield + setattr(i, f.name, {"_shredded": True}) + i.save() + + InvoiceLine.objects.filter( + attendee_name__isnull=False, + invoice__shredded=True + ).update(attendee_name="█") + + + +class Migration(migrations.Migration): + + dependencies = [ + ("pretixbase", "0310_question_valid_string_length_min"), + ] + + operations = [ + migrations.RunPython( + fix_unshredded_invoices, + migrations.RunPython.noop, + ), + ] diff --git a/src/pretix/base/shredder.py b/src/pretix/base/shredder.py index bc38e72ab9..90308820b3 100644 --- a/src/pretix/base/shredder.py +++ b/src/pretix/base/shredder.py @@ -50,8 +50,8 @@ from pretix.api.serializers.order import ( from pretix.api.serializers.waitinglist import WaitingListSerializer from pretix.base.i18n import LazyLocaleException from pretix.base.models import ( - CachedCombinedTicket, CachedTicket, Event, InvoiceAddress, OrderPayment, - OrderPosition, OrderRefund, OutgoingMail, QuestionAnswer, + CachedCombinedTicket, CachedTicket, Event, Invoice, InvoiceAddress, + OrderPayment, OrderPosition, OrderRefund, OutgoingMail, QuestionAnswer, ) from pretix.base.services.invoices import invoice_pdf_task from pretix.base.signals import register_data_shredders @@ -598,18 +598,30 @@ class InvoiceShredder(BaseDataShredder): def shred_data(self, progress_callback=None): qs_i = self.event.invoices.filter(shredded=False) total = qs_i.count() + ignore_fields = ( + 'prefix', 'invoice_no', 'full_invoice_no', 'invoice_from', 'invoice_from_name', 'invoice_from_zipcode', + 'invoice_from_city', 'invoice_from_state', 'invoice_from_country', 'invoice_from_tax_id', + 'invoice_from_vat_id', 'locale', 'payment_provider_stamp', 'footer_text', 'foreign_currency_display', + 'foreign_currency_source', 'transmission_type', 'transmission_provider', 'transmission_status', + ) for i in _progress_helper(qs_i, progress_callback, 0, total): if i.file: i.file.delete() - i.shredded = True - i.introductory_text = "█" - i.additional_text = "█" - i.invoice_to = "█" - i.payment_provider_text = "█" - i.transmission_info = {"_shredded": True} - i.save() - i.lines.update(description="█") + i.shredded = True + + for f in Invoice._meta.fields: + if f.name in ignore_fields: + continue + val = getattr(i, f.name, None) + if val and isinstance(val, str): + setattr(i, f.name, "█") + elif val and isinstance(val, list): # jsonfield + setattr(i, f.name, []) + elif val and isinstance(val, dict): # jsonfield + setattr(i, f.name, {"_shredded": True}) + i.save() + i.lines.update(description="█", attendee_name="█") class CachedTicketShredder(BaseDataShredder): diff --git a/src/tests/base/test_shredders.py b/src/tests/base/test_shredders.py index d2f0c59c8e..b5633d7e72 100644 --- a/src/tests/base/test_shredders.py +++ b/src/tests/base/test_shredders.py @@ -342,8 +342,11 @@ def test_invoice_shredder(event, order): s.shred_data() inv.refresh_from_db() - assert "Acme" not in inv.invoice_to - assert "icket" not in inv.lines.first().description + assert inv.invoice_to == "█" + assert inv.invoice_to_company == "█" + l = inv.lines.first() + assert l.description == "█" + assert l.attendee_name == "█" assert not inv.file assert not os.path.exists(fname)