diff --git a/src/pretix/api/serializers/organizer.py b/src/pretix/api/serializers/organizer.py index b207bbc57a..5f2fad27db 100644 --- a/src/pretix/api/serializers/organizer.py +++ b/src/pretix/api/serializers/organizer.py @@ -275,6 +275,7 @@ class OrganizerSettingsSerializer(SettingsSerializer): default_fields = [ 'customer_accounts', 'customer_accounts_link_by_email', + 'invoice_regenerate_allowed', 'contact_mail', 'imprint_url', 'organizer_info_text', diff --git a/src/pretix/api/views/order.py b/src/pretix/api/views/order.py index 8e4a8aafcb..ab0794805d 100644 --- a/src/pretix/api/views/order.py +++ b/src/pretix/api/views/order.py @@ -1451,8 +1451,14 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet): inv = self.get_object() if inv.canceled: raise ValidationError('The invoice has already been canceled.') + if not inv.event.settings.invoice_regenerate_allowed: + raise PermissionDenied('Invoices may not be changed after they are created.') elif inv.shredded: raise PermissionDenied('The invoice file is no longer stored on the server.') + elif inv.sent_to_organizer: + raise PermissionDenied('The invoice file has already been exported.') + elif now().astimezone(self.request.event.timezone).date() - inv.date > datetime.timedelta(days=1): + raise PermissionDenied('The invoice file is too old to be regenerated.') else: inv = regenerate_invoice(inv) inv.order.log_action( diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index d0f6199461..c0c49c8357 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -323,7 +323,7 @@ def generate_invoice(order: Order, trigger_pdf=True): order=order, event=order.event, organizer=order.event.organizer, - date=timezone.now().date(), + date=timezone.now().astimezone(order.event.timezone).date(), ) invoice = build_invoice(invoice) if trigger_pdf: diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index a01c62a0e6..b6e9b4c6c7 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -744,6 +744,18 @@ DEFAULTS = { "changes made through the backend."), ) }, + 'invoice_regenerate_allowed': { + 'default': 'False', + 'type': bool, + 'form_class': forms.BooleanField, + 'serializer_class': serializers.BooleanField, + 'form_kwargs': dict( + label=_("Allow to update existing invoices"), + help_text=_("By default, invoices can never again be changed once they are issued. In most countries, we " + "recommend to leave this option turned off and always issue a new invoice if a change needs " + "to be made."), + ) + }, 'invoice_generate_sales_channels': { 'default': json.dumps(['web']), 'type': list diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py index b4b43775f4..7badbb5a34 100644 --- a/src/pretix/control/forms/organizer.py +++ b/src/pretix/control/forms/organizer.py @@ -286,6 +286,7 @@ class OrganizerSettingsForm(SettingsForm): auto_fields = [ 'customer_accounts', 'customer_accounts_link_by_email', + 'invoice_regenerate_allowed', 'contact_mail', 'imprint_url', 'organizer_info_text', diff --git a/src/pretix/control/templates/pretixcontrol/order/index.html b/src/pretix/control/templates/pretixcontrol/order/index.html index a37eb767d2..9739df9578 100644 --- a/src/pretix/control/templates/pretixcontrol/order/index.html +++ b/src/pretix/control/templates/pretixcontrol/order/index.html @@ -236,14 +236,16 @@ {% if i.is_cancellation %}{% trans "Cancellation" context "invoice" %}{% else %}{% trans "Invoice" %}{% endif %} {{ i.number }} ({{ i.date|date:"SHORT_DATE_FORMAT" }}) {% if not i.canceled %} -
+ {% if request.event.settings.invoice_regenerate_allowed %} + + {% endif %} {% if not i.is_cancellation %}