Pluggable invoice transmission methods (#5020)

* Flexible invoice transmission

* UI work

* Add peppol and output

* API support

* Profile integration

* Simplify form for individuals

* Remove sent_to_customer usage

* more steps

* Revert "Bank transfer: Allow to send the invoice direclty to the accounting department (#2975)"

This reverts commit cea6c340be.

* minor fixes

* Fixes after rebase

* update stati

* Backend view

* Transmit and show status

* status, retransmission

* API retransmission

* More fields

* API docs

* Plugin docs

* Update migration

* Add missing license headers

* Remove dead code, fix current tests

* Run isort

* Update regex

* Rebase migration

* Fix migration

* Add tests, fix bugs

* Rebase migration

* Apply suggestion from @luelista

Co-authored-by: luelista <weller@rami.io>

* Apply suggestion from @luelista

Co-authored-by: luelista <weller@rami.io>

* Apply suggestion from @luelista

Co-authored-by: luelista <weller@rami.io>

* Apply suggestion from @luelista

Co-authored-by: luelista <weller@rami.io>

* Apply suggestion from @luelista

Co-authored-by: luelista <weller@rami.io>

* Make migration reversible

* Add TransmissionType.enforce_transmission

* Fix registries API usage after rebase

* Remove code I forgot to delete

* Update transmission status display depending on type

* Add testmode_supported

* Update src/pretix/static/pretixbase/js/addressform.js

Co-authored-by: luelista <weller@rami.io>

* Update src/pretix/static/pretixbase/js/addressform.js

Co-authored-by: luelista <weller@rami.io>

* Update src/pretix/static/pretixbase/js/addressform.js

Co-authored-by: luelista <weller@rami.io>

* New mechanism for non-required invoice forms

* Update src/pretix/base/invoicing/transmission.py

Co-authored-by: luelista <weller@rami.io>

* Declare testmode_supported for email

* Make transmission_email_other an implementation detail

* Fix failing tests and add new ones

* Update src/pretix/base/services/invoices.py

Co-authored-by: luelista <weller@rami.io>

* Add emails to email history

* Fix comma error

* More generic default email text

* Cleanup

* Remove "email invoices" button and refine logic

* Rebase migration

* Fix edge case

---------

Co-authored-by: luelista <weller@rami.io>
This commit is contained in:
Raphael Michel
2025-08-19 17:59:45 +02:00
committed by GitHub
co-authored by luelista
parent 37910f6037
commit 05c74b7ad6
65 changed files with 4514 additions and 1825 deletions
+160 -2
View File
@@ -26,6 +26,8 @@ invoice_from_country string Sender address:
invoice_from_tax_id string Sender address: Local Tax ID
invoice_from_vat_id string Sender address: EU VAT ID
invoice_to string Full recipient address
invoice_to_is_business boolean Recipient address: Business vs individual (``null`` for
invoices created before pretix 2025.6).
invoice_to_company string Recipient address: Company name
invoice_to_name string Recipient address: Person name
invoice_to_street string Recipient address: Address lines
@@ -35,6 +37,7 @@ invoice_to_state string Recipient addre
invoice_to_country string Recipient address: Country code
invoice_to_vat_id string Recipient address: EU VAT ID
invoice_to_beneficiary string Invoice beneficiary
invoice_to_transmission_info object Additional transmission info (see :ref:`rest-transmission-types`)
custom_field string Custom invoice address field
date date Invoice date
refers string Invoice number of an invoice this invoice refers to
@@ -110,6 +113,12 @@ foreign_currency_rate decimal (string) If ``foreign_cu
foreign_currency_rate_date date If ``foreign_currency_rate`` is set, this signifies the
date at which the currency rate was obtained.
internal_reference string Customer's reference to be printed on the invoice.
transmission_type string Requested transmission channel (see :ref:`rest-transmission-types`)
transmission_provider string Selected transmission provider (depends on installed
plugins). ``null`` if not yet chosen.
transmission_status string Transmission status, one of ``unknown`` (pre-2025.6),
``pending``, ``inflight``, ``failed``, and ``completed``.
transmission_date datetime Time of last change in transmission status (may be ``null``).
===================================== ========================== =======================================================
@@ -121,6 +130,76 @@ internal_reference string Customer's refe
The ``tax_code`` attribute has been added.
.. versionchanged:: 2025.6
The attributes ``invoice_to_is_business``, ``invoice_to_transmission_info``, ``transmission_type``,
``transmission_provider``, ``transmission_status``, and ``transmission_date`` have been added.
.. _`rest-transmission-types`:
Transmission types
------------------
pretix supports multiple ways to transmit an invoice from the organizer to the invoice recipient.
For each transmission type, different fields are supported in the ``transmission_info`` object of the
invoice address. Currently, pretix supports the following transmission types:
Email
"""""
The identifier ``"email"`` represents the transmission of PDF invoices through email.
This is the default transmission type in pretix and has some special behavior for backwards compatibility.
Transmission is always executed through the provider ``"email_pdf"``.
The ``transmission_info`` object may contain the following properties:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
transmission_email_address string Optional. An email address other than the order address
that the invoice should be sent to.
Business customers only.
===================================== ========================== =======================================================
PEPPOL
""""""
The identifier ``"peppol"`` represents the transmission of XML invoices through the `PEPPOL`_ network.
This is only available for business addresses.
This is not supported by pretix out of the box and requires the use of a suitable plugin.
The ``transmission_info`` object may contain the following properties:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
transmission_peppol_participant_id string Required. The PEPPOL participant ID of the recipient.
===================================== ========================== =======================================================
Italian Exchange System
"""""""""""""""""""""""
The identifier ``"it_sdi"`` represents the transmission of XML invoices through the `Sistema di Interscambio`_ network used in Italy.
This is only available for addresses with country ``"IT"``.
This is not supported by pretix out of the box and requires the use of a suitable plugin.
The ``transmission_info`` object may contain the following properties:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
transmission_it_sdi_codice_fiscale string Required for non-business address. Fiscal code of the
recipient.
transmission_it_sdi_pec string Required for business addresses. Address for certified
electronic mail.
transmission_it_sdi_recipient_code string Required for businesses. SdI recipient code.
===================================== ========================== =======================================================
If this type is selected, ``vat_id`` is required for business addresses.
List of all invoices
--------------------
@@ -164,6 +243,7 @@ List of all invoices
"invoice_from_vat_id":"",
"invoice_to": "Sample company\nJohn Doe\nTest street 12\n12345 Testington\nTestikistan\nVAT-ID: EU123456789",
"invoice_to_company": "Sample company",
"invoice_to_is_business": true,
"invoice_to_name": "John Doe",
"invoice_to_street": "Test street 12",
"invoice_to_zipcode": "12345",
@@ -172,6 +252,7 @@ List of all invoices
"invoice_to_country": "TE",
"invoice_to_vat_id": "EU123456789",
"invoice_to_beneficiary": "",
"invoice_to_transmission_info": {},
"custom_field": null,
"date": "2017-12-01",
"refers": null,
@@ -204,7 +285,11 @@ List of all invoices
],
"foreign_currency_display": "PLN",
"foreign_currency_rate": "4.2408",
"foreign_currency_rate_date": "2017-07-24"
"foreign_currency_rate_date": "2017-07-24",
"transmission_type": "email",
"transmission_provider": "email_pdf",
"transmission_status": "completed",
"transmission_date": "2017-07-24T10:00:00Z"
}
]
}
@@ -304,6 +389,7 @@ Fetching individual invoices
"invoice_from_vat_id":"",
"invoice_to": "Sample company\nJohn Doe\nTest street 12\n12345 Testington\nTestikistan\nVAT-ID: EU123456789",
"invoice_to_company": "Sample company",
"invoice_to_is_business": true,
"invoice_to_name": "John Doe",
"invoice_to_street": "Test street 12",
"invoice_to_zipcode": "12345",
@@ -312,6 +398,7 @@ Fetching individual invoices
"invoice_to_country": "TE",
"invoice_to_vat_id": "EU123456789",
"invoice_to_beneficiary": "",
"invoice_to_transmission_info": {},
"custom_field": null,
"date": "2017-12-01",
"refers": null,
@@ -344,7 +431,11 @@ Fetching individual invoices
],
"foreign_currency_display": "PLN",
"foreign_currency_rate": "4.2408",
"foreign_currency_rate_date": "2017-07-24"
"foreign_currency_rate_date": "2017-07-24",
"transmission_type": "email",
"transmission_provider": "email_pdf",
"transmission_status": "completed",
"transmission_date": "2017-07-24T10:00:00Z"
}
:param organizer: The ``slug`` field of the organizer to fetch
@@ -449,3 +540,70 @@ Invoices cannot be edited directly, but the following actions can be triggered:
:statuscode 400: The invoice has already been canceled
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
Transmitting invoices
---------------------
Invoices are transmitted automatically when created during order creation or payment receipt,
but in other cases transmission may need to be triggered manually.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(number)/transmit/
Transmits the invoice to the recipient, but only if it is in ``pending`` state.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/invoices/00001/transmit/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
Content-Type: application/pdf
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param number: The ``number`` field of the invoice to transmit
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to transmit this invoice **or** the invoice may not be transmitted
:statuscode 409: The invoice is currently in transmission
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(number)/retransmit/
Transmits the invoice to the recipient even if transmission was already attempted previously.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/invoices/00001/retransmit/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
Content-Type: application/pdf
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param number: The ``number`` field of the invoice to transmit
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to transmit this invoice **or** the invoice may not be transmitted
:statuscode 409: The invoice is currently in transmission
.. _PEPPOL: https://en.wikipedia.org/wiki/PEPPOL
.. _Sistema di Interscambio: https://it.wikipedia.org/wiki/Fattura_elettronica_in_Italia
+18 -3
View File
@@ -65,11 +65,16 @@ invoice_address object Invoice address
├ state string Customer state (ISO 3166-2 code). Only supported in
AU, BR, CA, CN, MY, MX, and US.
├ internal_reference string Customer's internal reference to be printed on the invoice
├ custom_field string Custom invoice address field
├ vat_id string Customer VAT ID
vat_id_validated string ``true``, if the VAT ID has been validated against the
vat_id_validated string ``true``, if the VAT ID has been validated against the
EU VAT service and validation was successful. This only
happens in rare cases.
├ transmission_type string Transmission channel for invoice (see also :ref:`rest-transmission-types`).
Defaults to ``email``.
└ transmission_info object Transmission-channel specific information (or ``null``).
See also :ref:`rest-transmission-types`.
positions list of objects List of order positions (see below). By default, only
non-canceled positions are included.
fees list of objects List of fees included in the order total. By default, only
@@ -142,6 +147,10 @@ plugin_data object Additional data
The ``plugin_data`` attribute has been added.
.. versionchanged:: 2025.6
The ``invoice_address.transmission_type`` and ``invoice_address.transmission_info`` attributes have been added.
.. _order-position-resource:
Order position resource
@@ -368,7 +377,9 @@ List of all orders
"state": "",
"internal_reference": "",
"vat_id": "EU123456789",
"vat_id_validated": false
"vat_id_validated": false,
"transmission_type": "email",
"transmission_info": {}
},
"positions": [
{
@@ -610,7 +621,9 @@ Fetching individual orders
"state": "",
"internal_reference": "",
"vat_id": "EU123456789",
"vat_id_validated": false
"vat_id_validated": false,
"transmission_type": "email",
"transmission_info": {}
},
"positions": [
{
@@ -1017,6 +1030,8 @@ Creating orders
* ``vat_id_validated`` (optional) If you need support for reverse charge (rarely the case), you need to check
yourself if the passed VAT ID is a valid EU VAT ID. In that case, set this to ``true``. Only valid VAT IDs will
trigger reverse charge taxation. Don't forget to set ``is_business`` as well!
* ``transmission_type`` (optional, defaults to ``email``)
* ``transmission_info`` (optional, see also :ref:`rest-transmission-types`)
* ``positions``