diff --git a/src/pretix/base/models/tax.py b/src/pretix/base/models/tax.py
index cd14b0c37b..fb94ed0229 100644
--- a/src/pretix/base/models/tax.py
+++ b/src/pretix/base/models/tax.py
@@ -5,8 +5,9 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.utils.formats import localize
from django.utils.timezone import get_current_timezone, now
-from django.utils.translation import gettext_lazy as _
+from django.utils.translation import gettext_lazy as _, pgettext
from i18nfield.fields import I18nCharField
+from i18nfield.strings import LazyI18nString
from pretix.base.decimal import round_decimal
from pretix.base.models.base import LoggedModel
@@ -268,6 +269,25 @@ class TaxRule(LoggedModel):
return r
return {'action': 'vat'}
+ def invoice_text(self, invoice_address):
+ if self._custom_rules:
+ rule = self.get_matching_rule(invoice_address)
+ t = rule.get('invoice_text', {})
+ if t and any(l for l in t.values()):
+ return str(LazyI18nString(t))
+ if self.is_reverse_charge(invoice_address):
+ if is_eu_country(invoice_address.country):
+ return pgettext(
+ "invoice",
+ "Reverse Charge: According to Article 194, 196 of Council Directive 2006/112/EEC, VAT liability "
+ "rests with the service recipient."
+ )
+ else:
+ return pgettext(
+ "invoice",
+ "VAT liability rests with the service recipient."
+ )
+
def is_reverse_charge(self, invoice_address):
if self._custom_rules:
rule = self.get_matching_rule(invoice_address)
diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py
index 1de867a60a..69c8ec4bb4 100644
--- a/src/pretix/base/services/invoices.py
+++ b/src/pretix/base/services/invoices.py
@@ -24,7 +24,7 @@ from pretix.base.i18n import language
from pretix.base.models import (
Invoice, InvoiceAddress, InvoiceLine, Order, OrderFee,
)
-from pretix.base.models.tax import EU_CURRENCIES, is_eu_country
+from pretix.base.models.tax import EU_CURRENCIES
from pretix.base.services.tasks import TransactionAwareTask
from pretix.base.settings import GlobalSettingsObject
from pretix.base.signals import invoice_line_text, periodic_task
@@ -142,6 +142,8 @@ def build_invoice(invoice: Invoice) -> Invoice:
reverse_charge = False
positions.sort(key=lambda p: p.sort_key)
+
+ tax_texts = []
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
@@ -178,22 +180,10 @@ def build_invoice(invoice: Invoice) -> Invoice:
if p.tax_rule and p.tax_rule.is_reverse_charge(ia) and p.price and not p.tax_value:
reverse_charge = True
- if reverse_charge:
- if invoice.additional_text:
- invoice.additional_text += "
"
- if is_eu_country(invoice.invoice_to_country):
- invoice.additional_text += pgettext(
- "invoice",
- "Reverse Charge: According to Article 194, 196 of Council Directive 2006/112/EEC, VAT liability "
- "rests with the service recipient."
- )
- else:
- invoice.additional_text += pgettext(
- "invoice",
- "VAT liability rests with the service recipient."
- )
- invoice.reverse_charge = True
- invoice.save()
+ if p.tax_rule:
+ tax_text = p.tax_rule.invoice_text(ia)
+ if tax_text and tax_text not in tax_texts:
+ tax_texts.append(tax_text)
offset = len(positions)
for i, fee in enumerate(invoice.order.fees.all()):
@@ -213,6 +203,20 @@ def build_invoice(invoice: Invoice) -> Invoice:
tax_name=fee.tax_rule.name if fee.tax_rule else ''
)
+ if fee.tax_rule and fee.tax_rule.is_reverse_charge(ia) and fee.value and not fee.tax_value:
+ reverse_charge = True
+
+ if fee.tax_rule:
+ tax_text = fee.tax_rule.invoice_text(ia)
+ if tax_text and tax_text not in tax_texts:
+ tax_texts.append(tax_text)
+
+ if tax_texts:
+ invoice.additional_text += "
"
+ invoice.additional_text += "
".join(tax_texts)
+ invoice.reverse_charge = reverse_charge
+ invoice.save()
+
return invoice
diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py
index f51efd3176..c630cdfe7d 100644
--- a/src/pretix/control/forms/event.py
+++ b/src/pretix/control/forms/event.py
@@ -19,7 +19,9 @@ from pytz import common_timezones, timezone
from pretix.base.channels import get_all_sales_channels
from pretix.base.email import get_available_placeholders
-from pretix.base.forms import I18nModelForm, PlaceholderValidator, SettingsForm
+from pretix.base.forms import (
+ I18nModelForm, PlaceholderValidator, SettingsForm,
+)
from pretix.base.models import Event, Organizer, TaxRule, Team
from pretix.base.models.event import EventMetaValue, SubEvent
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
@@ -1099,7 +1101,7 @@ class CountriesAndEU(CachedCountries):
cache_subkey = 'with_any_or_eu'
-class TaxRuleLineForm(forms.Form):
+class TaxRuleLineForm(I18nForm):
country = LazyTypedChoiceField(
choices=CountriesAndEU(),
required=False
@@ -1126,10 +1128,25 @@ class TaxRuleLineForm(forms.Form):
max_digits=10, decimal_places=2,
required=False
)
+ invoice_text = I18nFormField(
+ label=_('Text on invoice'),
+ required=False,
+ widget=I18nTextInput
+ )
+
+
+class I18nBaseFormSet(I18nFormSetMixin, forms.BaseFormSet):
+ # compatibility shim for django-i18nfield library
+
+ def __init__(self, *args, **kwargs):
+ self.event = kwargs.pop('event', None)
+ if self.event:
+ kwargs['locales'] = self.event.settings.get('locales')
+ super().__init__(*args, **kwargs)
TaxRuleLineFormSet = formset_factory(
- TaxRuleLineForm,
+ TaxRuleLineForm, formset=I18nBaseFormSet,
can_order=True, can_delete=True, extra=0
)
diff --git a/src/pretix/control/templates/pretixcontrol/event/tax_edit.html b/src/pretix/control/templates/pretixcontrol/event/tax_edit.html
index 98651dc67e..3864ce4d85 100644
--- a/src/pretix/control/templates/pretixcontrol/event/tax_edit.html
+++ b/src/pretix/control/templates/pretixcontrol/event/tax_edit.html
@@ -61,10 +61,10 @@
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
-