diff --git a/doc/development/api/general.rst b/doc/development/api/general.rst index 636de683cd..ce0bcf0dc6 100644 --- a/doc/development/api/general.rst +++ b/doc/development/api/general.rst @@ -23,7 +23,7 @@ There are multiple signals that will be sent out in the ordering cycle: .. automodule:: pretix.base.signals :no-index: - :members: validate_cart, validate_cart_addons, validate_order, order_valid_if_pending, order_fee_calculation, order_paid, order_placed, order_canceled, order_reactivated, order_expired, order_expiry_changed, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download, order_split, order_gracefully_delete, invoice_line_text + :members: validate_cart, validate_cart_addons, validate_order, order_valid_if_pending, order_fee_calculation, order_paid, order_placed, order_canceled, order_reactivated, order_expired, order_expiry_changed, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download, order_split, order_gracefully_delete, build_invoice_data, invoice_line_text Check-ins """"""""" diff --git a/src/pretix/base/migrations/0290_invoice_plugin_data.py b/src/pretix/base/migrations/0290_invoice_plugin_data.py new file mode 100644 index 0000000000..ca0f9d4e1a --- /dev/null +++ b/src/pretix/base/migrations/0290_invoice_plugin_data.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.17 on 2025-09-09 09:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("pretixbase", "0289_invoiceline_period"), + ] + + operations = [ + migrations.AddField( + model_name="invoice", + name="plugin_data", + field=models.JSONField(default=dict), + ), + ] diff --git a/src/pretix/base/models/invoices.py b/src/pretix/base/models/invoices.py index d9401f8578..7457d8d02d 100644 --- a/src/pretix/base/models/invoices.py +++ b/src/pretix/base/models/invoices.py @@ -202,6 +202,7 @@ class Invoice(models.Model): transmission_info = models.JSONField(null=True, blank=True) file = models.FileField(null=True, blank=True, upload_to=invoice_filename, max_length=255) + plugin_data = models.JSONField(default=dict) objects = ScopedManager(organizer='event__organizer') diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index a47be9fef2..266a8d63b2 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -61,7 +61,9 @@ from pretix.base.models.tax import EU_CURRENCIES from pretix.base.services.tasks import ( TransactionAwareProfiledEventTask, TransactionAwareTask, ) -from pretix.base.signals import invoice_line_text, periodic_task +from pretix.base.signals import ( + build_invoice_data, invoice_line_text, periodic_task, +) from pretix.celery_app import app from pretix.helpers.database import OF_SELF, rolledback_transaction from pretix.helpers.models import modelcopy @@ -362,6 +364,7 @@ def build_invoice(invoice: Invoice) -> Invoice: invoice.reverse_charge = reverse_charge invoice.save() + build_invoice_data.send(sender=invoice.event, invoice=invoice) return invoice diff --git a/src/pretix/base/signals.py b/src/pretix/base/signals.py index 8e18422008..d42188d6af 100644 --- a/src/pretix/base/signals.py +++ b/src/pretix/base/signals.py @@ -596,6 +596,18 @@ multiple events. Receivers should return a subclass of pretix.base.exporter.Base The ``sender`` keyword argument will contain an organizer. """ +build_invoice_data = EventPluginSignal() +""" +Arguments: ``invoice`` + +This signal is sent out every time an invoice is built, after the invoice model was created +and filled and before the PDF generation task is started. You can use this to make changes +to the invoice, but we recommend to mostly use it to add content to ``Invoice.plugin_data``. +You are responsible for saving any changes to the database. + +As with all event-plugin signals, the ``sender`` keyword argument will contain the event. +""" + validate_order = EventPluginSignal() """ Arguments: ``payments``, ``positions``, ``email``, ``locale``, ``invoice_address``,