diff --git a/doc/api/resources/invoices.rst b/doc/api/resources/invoices.rst
index 5dde127c32..68e166d029 100644
--- a/doc/api/resources/invoices.rst
+++ b/doc/api/resources/invoices.rst
@@ -78,6 +78,12 @@ lines list of objects The actual invo
an event series not created by a product (e.g. shipping or
cancellation fees) as well as whenever the respective (sub)event
has no end date set.
+├ event_location string Location of the (sub)event this line was created for as it
+ was set during invoice creation. Can be ``null`` for all invoice
+ lines created before this was introduced as well as for lines in
+ an event series not created by a product (e.g. shipping or
+ cancellation fees) as well as whenever the respective (sub)event
+ has no location set.
├ attendee_name string Attendee name at time of invoice creation. Can be ``null`` if no
name was set or if names are configured to not be added to invoices.
├ gross_value money (string) Price including taxes
@@ -110,6 +116,10 @@ internal_reference string Customer's refe
The attributes ``fee_type`` and ``fee_internal_type`` have been added.
+.. versionchanged:: 4.1
+
+ The attribute ``lines.event_location`` has been added.
+
Endpoints
---------
@@ -179,6 +189,7 @@ Endpoints
"fee_internal_type": null,
"event_date_from": "2017-12-27T10:00:00Z",
"event_date_to": null,
+ "event_location": "Heidelberg",
"attendee_name": null,
"gross_value": "23.00",
"tax_value": "0.00",
@@ -267,6 +278,7 @@ Endpoints
"fee_internal_type": null,
"event_date_from": "2017-12-27T10:00:00Z",
"event_date_to": null,
+ "event_location": "Heidelberg",
"attendee_name": null,
"gross_value": "23.00",
"tax_value": "0.00",
diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py
index 8793d07627..c1eca9204c 100644
--- a/src/pretix/api/serializers/event.py
+++ b/src/pretix/api/serializers/event.py
@@ -734,6 +734,7 @@ class EventSettingsSerializer(SettingsSerializer):
'invoice_numbers_prefix_cancellations',
'invoice_numbers_counter_length',
'invoice_attendee_name',
+ 'invoice_event_location',
'invoice_include_expire_date',
'invoice_address_explanation_text',
'invoice_email_attachment',
diff --git a/src/pretix/api/serializers/order.py b/src/pretix/api/serializers/order.py
index 5c1168aa2a..17a14eb642 100644
--- a/src/pretix/api/serializers/order.py
+++ b/src/pretix/api/serializers/order.py
@@ -1428,7 +1428,7 @@ class InlineInvoiceLineSerializer(I18nAwareModelSerializer):
model = InvoiceLine
fields = ('position', 'description', 'item', 'variation', 'attendee_name', 'event_date_from',
'event_date_to', 'gross_value', 'tax_value', 'tax_rate', 'tax_name', 'fee_type',
- 'fee_internal_type')
+ 'fee_internal_type', 'event_location')
class InvoiceSerializer(I18nAwareModelSerializer):
diff --git a/src/pretix/base/exporters/invoices.py b/src/pretix/base/exporters/invoices.py
index a5f0096acc..ab39e8a1b6 100644
--- a/src/pretix/base/exporters/invoices.py
+++ b/src/pretix/base/exporters/invoices.py
@@ -324,7 +324,6 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
_('Tax rate'),
_('Tax name'),
_('Event start date'),
-
_('Date'),
_('Order code'),
_('E-mail address'),
@@ -348,6 +347,8 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
_('Invoice recipient:') + ' ' + _('Beneficiary'),
_('Invoice recipient:') + ' ' + _('Internal reference'),
_('Payment providers'),
+ _('Event end date'),
+ _('Location'),
]
p_providers = OrderPayment.objects.filter(
@@ -406,7 +407,9 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
', '.join([
str(self.providers.get(p, p)) for p in sorted(set((l.payment_providers or '').split(',')))
if p and p != 'free'
- ])
+ ]),
+ date_format(l.event_date_to, "SHORT_DATE_FORMAT") if l.event_date_to else "",
+ l.event_location or "",
]
@cached_property
diff --git a/src/pretix/base/migrations/0201_invoiceline_event_location.py b/src/pretix/base/migrations/0201_invoiceline_event_location.py
new file mode 100644
index 0000000000..e69f7e6df5
--- /dev/null
+++ b/src/pretix/base/migrations/0201_invoiceline_event_location.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.4 on 2021-11-03 09:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('pretixbase', '0200_transaction'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='invoiceline',
+ name='event_location',
+ field=models.TextField(null=True),
+ ),
+ ]
diff --git a/src/pretix/base/models/invoices.py b/src/pretix/base/models/invoices.py
index 836bccdf7e..ece97d6f96 100644
--- a/src/pretix/base/models/invoices.py
+++ b/src/pretix/base/models/invoices.py
@@ -329,6 +329,8 @@ class InvoiceLine(models.Model):
:type event_date_from: datetime
:param event_date_to: Event end date of the (sub)event at the time the invoice was created
:type event_date_to: datetime
+ :param event_location: Event location of the (sub)event at the time the invoice was created
+ :type event_location: str
:param item: The item this line refers to
:type item: Item
:param variation: The variation this line refers to
@@ -346,6 +348,7 @@ class InvoiceLine(models.Model):
subevent = models.ForeignKey('SubEvent', null=True, blank=True, on_delete=models.PROTECT)
event_date_from = models.DateTimeField(null=True)
event_date_to = models.DateTimeField(null=True)
+ event_location = models.TextField(null=True, blank=True)
item = models.ForeignKey('Item', null=True, blank=True, on_delete=models.PROTECT)
variation = models.ForeignKey('ItemVariation', null=True, blank=True, on_delete=models.PROTECT)
attendee_name = models.TextField(null=True, blank=True)
diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py
index 7de971b41e..333cfe7d4d 100644
--- a/src/pretix/base/services/invoices.py
+++ b/src/pretix/base/services/invoices.py
@@ -69,6 +69,10 @@ from pretix.helpers.models import modelcopy
logger = logging.getLogger(__name__)
+def _location_oneliner(loc):
+ return ', '.join([l.strip() for l in loc.splitlines() if l and l.strip()])
+
+
@transaction.atomic
def build_invoice(invoice: Invoice) -> Invoice:
invoice.locale = invoice.event.settings.get('invoice_language', invoice.event.settings.locale)
@@ -176,19 +180,38 @@ def build_invoice(invoice: Invoice) -> Invoice:
reverse_charge = False
positions.sort(key=lambda p: p.sort_key)
+ fees = list(invoice.order.fees.all())
+
+ locations = {
+ str((p.subevent or invoice.event).location) if (p.subevent or invoice.event).location else None
+ for p in positions
+ }
+ if fees and invoice.event.has_subevents:
+ locations.add(None)
tax_texts = []
+
+ if invoice.event.settings.invoice_event_location and len(locations) == 1 and list(locations)[0] is not None:
+ tax_texts.append(pgettext("invoice", "Event location: {location}").format(
+ location=_location_oneliner(str(list(locations)[0]))
+ ))
+
for i, p in enumerate(positions):
if not invoice.event.settings.invoice_include_free and p.price == Decimal('0.00') and not p.addon_c:
continue
+ location = str((p.subevent or invoice.event).location) if (p.subevent or invoice.event).location else None
+
desc = str(p.item.name)
if p.variation:
desc += " - " + str(p.variation.value)
if p.addon_to_id:
desc = " + " + desc
if invoice.event.settings.invoice_attendee_name and p.attendee_name:
- desc += "
" + pgettext("invoice", "Attendee: {name}").format(name=p.attendee_name)
+ desc += "
" + pgettext("invoice", "Attendee: {name}").format(
+ name=p.attendee_name
+ )
+
for recv, resp in invoice_line_text.send(sender=invoice.event, position=p):
if resp:
desc += "
" + resp
@@ -204,6 +227,12 @@ def build_invoice(invoice: Invoice) -> Invoice:
if invoice.event.has_subevents:
desc += "
" + pgettext("subevent", "Date: {}").format(p.subevent)
+
+ if invoice.event.settings.invoice_event_location and location and len(locations) > 1:
+ desc += "
" + pgettext("invoice", "Event location: {location}").format(
+ location=_location_oneliner(location)
+ )
+
InvoiceLine.objects.create(
position=i,
invoice=invoice,
@@ -216,6 +245,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
attendee_name=p.attendee_name if invoice.event.settings.invoice_attendee_name else None,
event_date_from=p.subevent.date_from if invoice.event.has_subevents else invoice.event.date_from,
event_date_to=p.subevent.date_to if invoice.event.has_subevents else invoice.event.date_to,
+ event_location=location if invoice.event.settings.invoice_event_location else None,
tax_rate=p.tax_rate, tax_name=p.tax_rule.name if p.tax_rule else ''
)
@@ -228,7 +258,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
tax_texts.append(tax_text)
offset = len(positions)
- for i, fee in enumerate(invoice.order.fees.all()):
+ for i, fee in enumerate(fees):
if fee.fee_type == OrderFee.FEE_TYPE_OTHER and fee.description:
fee_title = fee.description
else:
@@ -242,6 +272,12 @@ def build_invoice(invoice: Invoice) -> Invoice:
gross_value=fee.value,
event_date_from=None if invoice.event.has_subevents else invoice.event.date_from,
event_date_to=None if invoice.event.has_subevents else invoice.event.date_to,
+ event_location=(
+ None if invoice.event.has_subevents
+ else (str(invoice.event.location)
+ if invoice.event.settings.invoice_event_location and invoice.event.location
+ else None)
+ ),
tax_value=fee.tax_value,
tax_rate=fee.tax_rate,
tax_name=fee.tax_rule.name if fee.tax_rule else '',
diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py
index f5937b2edf..9c57ba4d38 100644
--- a/src/pretix/base/settings.py
+++ b/src/pretix/base/settings.py
@@ -310,6 +310,17 @@ DEFAULTS = {
label=_("Show attendee names on invoices"),
)
},
+ 'invoice_event_location': {
+ 'default': 'False',
+ 'type': bool,
+ 'form_class': forms.BooleanField,
+ 'serializer_class': serializers.BooleanField,
+ 'form_kwargs': dict(
+ label=_("Show event location on invoices"),
+ help_text=_("The event location will be shown below the list of products if it is the same for all "
+ "lines. It will be shown on every line if there are different locations.")
+ )
+ },
'invoice_eu_currencies': {
'default': 'True',
'type': bool,
diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py
index 83e331acd9..7d8cc2bc7d 100644
--- a/src/pretix/control/forms/event.py
+++ b/src/pretix/control/forms/event.py
@@ -751,6 +751,7 @@ class InvoiceSettingsForm(SettingsForm):
'invoice_reissue_after_modify',
'invoice_generate',
'invoice_attendee_name',
+ 'invoice_event_location',
'invoice_include_expire_date',
'invoice_numbers_consecutive',
'invoice_numbers_prefix',
diff --git a/src/pretix/control/templates/pretixcontrol/event/invoicing.html b/src/pretix/control/templates/pretixcontrol/event/invoicing.html
index d05f0112c8..f573889f6e 100644
--- a/src/pretix/control/templates/pretixcontrol/event/invoicing.html
+++ b/src/pretix/control/templates/pretixcontrol/event/invoicing.html
@@ -48,6 +48,7 @@
{% bootstrap_field form.invoice_renderer layout="control" %}
{% bootstrap_field form.invoice_attendee_name layout="control" %}
+ {% bootstrap_field form.invoice_event_location layout="control" %}
{% bootstrap_field form.invoice_include_expire_date layout="control" %}
{% bootstrap_field form.invoice_introductory_text layout="control" %}
{% bootstrap_field form.invoice_additional_text layout="control" %}
diff --git a/src/tests/api/test_orders.py b/src/tests/api/test_orders.py
index 502c1e8696..ad690604ac 100644
--- a/src/tests/api/test_orders.py
+++ b/src/tests/api/test_orders.py
@@ -1022,6 +1022,7 @@ TEST_INVOICE_RES = {
"description": "Budget Ticket
Attendee: Peter",
'event_date_from': '2017-12-27T10:00:00Z',
'event_date_to': None,
+ 'event_location': None,
'attendee_name': 'Peter',
'item': None,
'variation': None,
@@ -1037,6 +1038,7 @@ TEST_INVOICE_RES = {
"description": "Payment fee",
'event_date_from': '2017-12-27T10:00:00Z',
'event_date_to': None,
+ 'event_location': None,
'attendee_name': None,
'fee_type': "payment",
'fee_internal_type': None,
@@ -4440,6 +4442,7 @@ def test_order_create_invoice(token_client, organizer, event, order):
'description': 'Budget Ticket
Attendee: Peter',
'event_date_from': '2017-12-27T10:00:00Z',
'event_date_to': None,
+ 'event_location': None,
'fee_type': None,
'fee_internal_type': None,
'attendee_name': 'Peter',
@@ -4455,6 +4458,7 @@ def test_order_create_invoice(token_client, organizer, event, order):
'description': 'Payment fee',
'event_date_from': '2017-12-27T10:00:00Z',
'event_date_to': None,
+ 'event_location': None,
'fee_type': "payment",
'fee_internal_type': None,
'attendee_name': None,