mirror of
https://github.com/pretix/pretix.git
synced 2026-05-06 15:24:02 +00:00
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:
@@ -1199,6 +1199,20 @@ class MailSettingsForm(FormPlaceholderMixin, SettingsForm):
|
||||
required=False,
|
||||
widget=I18nMarkdownTextarea,
|
||||
)
|
||||
mail_subject_order_invoice = I18nFormField(
|
||||
label=_("Subject"),
|
||||
required=False,
|
||||
widget=I18nTextInput,
|
||||
help_text=_("This will only be used if the invoice is sent to a different email address or at a different time "
|
||||
"than the order confirmation."),
|
||||
)
|
||||
mail_text_order_invoice = I18nFormField(
|
||||
label=_("Text"),
|
||||
required=False,
|
||||
widget=I18nMarkdownTextarea,
|
||||
help_text=_("This will only be used if the invoice is sent to a different email address or at a different time "
|
||||
"than the order confirmation."),
|
||||
)
|
||||
mail_subject_download_reminder = I18nFormField(
|
||||
label=_("Subject sent to order contact address"),
|
||||
required=False,
|
||||
@@ -1350,6 +1364,8 @@ class MailSettingsForm(FormPlaceholderMixin, SettingsForm):
|
||||
'mail_text_order_payment_failed': ['event', 'order'],
|
||||
'mail_subject_order_payment_failed': ['event', 'order'],
|
||||
'mail_text_order_custom_mail': ['event', 'order'],
|
||||
'mail_text_order_invoice': ['event', 'order', 'invoice'],
|
||||
'mail_subject_order_invoice': ['event', 'order', 'invoice'],
|
||||
'mail_text_download_reminder': ['event', 'order'],
|
||||
'mail_subject_download_reminder': ['event', 'order'],
|
||||
'mail_text_download_reminder_attendee': ['event', 'order', 'position'],
|
||||
|
||||
@@ -524,6 +524,11 @@ def pretixcontrol_orderposition_blocked_display(sender: Event, orderposition, bl
|
||||
'pretix.event.order.invoice.generated': _('The invoice has been generated.'),
|
||||
'pretix.event.order.invoice.regenerated': _('The invoice has been regenerated.'),
|
||||
'pretix.event.order.invoice.reissued': _('The invoice has been reissued.'),
|
||||
'pretix.event.order.invoice.sent': _('The invoice {full_invoice_no} has been sent.'),
|
||||
'pretix.event.order.invoice.sending_failed': _('The transmission of invoice {full_invoice_no} has failed.'),
|
||||
'pretix.event.order.invoice.testmode_ignored': _('Invoice {full_invoice_no} has not been transmitted because '
|
||||
'no transmission provider supports test mode invoices.'),
|
||||
'pretix.event.order.invoice.retransmitted': _('The invoice {full_invoice_no} has been scheduled for retransmission.'),
|
||||
'pretix.event.order.comment': _('The order\'s internal comment has been updated.'),
|
||||
'pretix.event.order.custom_followup_at': _('The order\'s follow-up date has been updated.'),
|
||||
'pretix.event.order.checkin_attention': _('The order\'s flag to require attention at check-in has been '
|
||||
@@ -536,6 +541,7 @@ def pretixcontrol_orderposition_blocked_display(sender: Event, orderposition, bl
|
||||
'pretix.event.order.email.error': _('Sending of an email has failed.'),
|
||||
'pretix.event.order.email.attachments.skipped': _('The email has been sent without attached tickets since they '
|
||||
'would have been too large to be likely to arrive.'),
|
||||
'pretix.event.order.email.invoice': _('An invoice email has been sent.'),
|
||||
'pretix.event.order.email.custom_sent': _('A custom email has been sent.'),
|
||||
'pretix.event.order.position.email.custom_sent': _('A custom email has been sent to an attendee.'),
|
||||
'pretix.event.order.email.download_reminder_sent': _('An email has been sent with a reminder that the ticket '
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% extends "pretixcontrol/event/settings_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% load getitem %}
|
||||
{% block inside %}
|
||||
<h1>{% trans "Invoice settings" %}</h1>
|
||||
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
|
||||
@@ -59,6 +60,99 @@
|
||||
{% bootstrap_field form.invoice_renderer_highlight_order_code layout="control" %}
|
||||
{% bootstrap_field form.invoice_eu_currencies layout="control" %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Invoice transmission" %}</legend>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
pretix can transmit invoices using different transmission methods. Different transmission methods
|
||||
might be required depending on country and industry. By default, sending invoices as PDF files
|
||||
via email is always available. Other types of transmission can be added by plugins.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Whether a transmission method listed here is actually selectable for customers may depend on
|
||||
the country of the customer or whether the customer is entering a business address.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Transmission method" %}</th>
|
||||
<th>{% trans "Status" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for t in transmission_types %}
|
||||
{% if transmission_providers|getitem:t.identifier %}
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="colgroup" class="text-muted">
|
||||
{{ t.verbose_name }}
|
||||
</th>
|
||||
<th>
|
||||
{% if ready|getitem:t.identifier %}
|
||||
<span class="text-success">
|
||||
<span class="fa fa-check fa-fw"></span>
|
||||
{% trans "Available" %}
|
||||
{% if t.exclusive %}
|
||||
<span data-toggle="tooltip" title="{% trans "When this type is available for an invoice address, no other type can be selected." %}">
|
||||
{% trans "(exclusive)" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-muted">
|
||||
<span class="fa fa-ban fa-fw"></span>
|
||||
{% trans "Unavailable" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
{% for p, is_ready, settings_url in transmission_providers|getitem:t.identifier %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ p.verbose_name }}
|
||||
</td>
|
||||
<td>
|
||||
{% if is_ready %}
|
||||
<span class="text-success">
|
||||
<span class="fa fa-check fa-fw"></span>
|
||||
{% trans "Available" %}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-muted">
|
||||
<span class="fa fa-ban fa-fw"></span>
|
||||
{% trans "Not configured" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
{% if settings_url %}
|
||||
<a href="{{ settings_url }}" class="btn btn-default">
|
||||
<span class="fa fa-cog" aria-hidden="true"></span>
|
||||
{% trans "Settings" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
<p>
|
||||
{% url "control:event.settings.plugins" event=request.event.slug organizer=request.organizer.slug as plugin_settings_url %}
|
||||
<a href="{{ plugin_settings_url }}" class="btn btn-default">
|
||||
<i class="fa fa-plus"></i> {% trans "Enable additional invoice transmission plugins" %}
|
||||
</a>
|
||||
</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-default btn-lg" name="preview" value="preview" formtarget="_blank">
|
||||
|
||||
@@ -117,6 +117,9 @@
|
||||
{% blocktrans asvar title_order_custom_mail %}Order custom mail{% endblocktrans %}
|
||||
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="custom_mail" title=title_order_custom_mail items="mail_text_order_custom_mail" %}
|
||||
|
||||
{% blocktrans asvar title_order_custom_mail %}Invoice{% endblocktrans %}
|
||||
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_invoice" title=title_order_custom_mail items="mail_subject_order_invoice,mail_text_order_invoice" %}
|
||||
|
||||
{% blocktrans asvar title_download_tickets_reminder %}Reminder to download tickets{% endblocktrans %}
|
||||
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="ticket_reminder" title=title_download_tickets_reminder items="mail_days_download_reminder,mail_subject_download_reminder,mail_text_download_reminder,mail_send_download_reminder_attendee,mail_subject_download_reminder_attendee,mail_text_download_reminder_attendee,mail_sales_channel_download_reminder" exclude="mail_days_download_reminder,mail_send_download_reminder_attendee,mail_sales_channel_download_reminder" %}
|
||||
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
{% endif %}</strong>
|
||||
</h4>
|
||||
</summary>
|
||||
<div id="invoice">
|
||||
<div id="invoice"
|
||||
data-address-information-url="{% url "js_helpers.address_form" %}?invoice=true&organizer={{ request.event.organizer.slug|urlencode }}&event={{ request.event.slug|urlencode }}">
|
||||
<div class="panel-body">
|
||||
{% bootstrap_form invoice_form layout="horizontal" %}
|
||||
</div>
|
||||
|
||||
@@ -271,24 +271,70 @@
|
||||
<a href="{% url "control:event.invoice.download" invoice=i.pk event=request.event.slug organizer=request.event.organizer.slug %}" target="_blank">
|
||||
{% if i.is_cancellation %}{% trans "Cancellation" context "invoice" %}{% else %}{% trans "Invoice" %}{% endif %}
|
||||
{{ i.number }}</a>
|
||||
({{ i.date|date:"SHORT_DATE_FORMAT" }})
|
||||
{% if i.sent_to_customer.year == 1970 %}
|
||||
<span class="fa-stack fa-stack-small" data-toggle="tooltip" title="{% trans "We don't know if this invoice was emailed to the customer since it was created before our system tracked this information" %}">
|
||||
({{ i.date|date:"SHORT_DATE_FORMAT" }}, {{ i.transmission_type_instance.verbose_name }})
|
||||
{% if i.transmission_status == "unknown" %}
|
||||
{# Legacy invoice, before the introduction of transmission status #}
|
||||
{% if i.transmission_date.year == 1970 %}
|
||||
<span class="fa-stack fa-stack-small" data-toggle="tooltip" title="{% trans "We don't know if this invoice was emailed to the customer since it was created before our system tracked this information" %}">
|
||||
<span class="fa fa-background fa-envelope text-muted fa-stack-1x"></span>
|
||||
<span class="fa fa-question fa-stack-1x fa-stack-shifted"></span>
|
||||
</span>
|
||||
{% elif i.transmission_date %}
|
||||
<span class="fa-stack fa-stack-small" data-toggle="tooltip" title="{% trans "Invoice was emailed to customer" %}">
|
||||
<span class="fa fa-background fa-envelope text-muted fa-stack-1x"></span>
|
||||
<span class="fa fa-check text-success fa-stack-1x fa-stack-shifted"></span>
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="fa-stack fa-stack-small" data-toggle="tooltip" title="{% trans "Invoice was not yet emailed to customer" %}">
|
||||
<span class="fa fa-background fa-envelope text-muted fa-stack-1x"></span>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% elif i.transmission_status == "pending" %}
|
||||
{% if i.transmission_type_instance.enforce_transmission %}
|
||||
<span class="fa-stack fa-stack-small" data-toggle="tooltip" title="{% trans "Invoice is scheduled to be transmitted" %}">
|
||||
<span class="fa fa-background fa-envelope text-muted fa-stack-1x"></span>
|
||||
<span class="fa fa-clock-o text-success fa-stack-1x fa-stack-shifted"></span>
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="fa fa-envelope fa-background text-muted" data-toggle="tooltip" title="{% trans "Invoice is not yet transmitted" %}"></span>
|
||||
{% endif %}
|
||||
{% elif i.transmission_status == "inflight" %}
|
||||
<span class="fa-stack fa-stack-small" data-toggle="tooltip" title="{% trans "Invoice is currently in transmission" %}">
|
||||
<span class="fa fa-background fa-envelope text-muted fa-stack-1x"></span>
|
||||
<span class="fa fa-question fa-stack-1x fa-stack-shifted"></span>
|
||||
<span class="fa fa-send text-success fa-stack-1x fa-stack-shifted"></span>
|
||||
</span>
|
||||
{% elif i.sent_to_customer %}
|
||||
<span class="fa-stack fa-stack-small" data-toggle="tooltip" title="{% trans "Invoice was emailed to customer" %}">
|
||||
{% elif i.transmission_status == "testmode_ignored" %}
|
||||
<span class="fa-stack fa-stack-small" data-toggle="tooltip" title="{% trans "Invoice not transmitted in test mode" %}">
|
||||
<span class="fa fa-background fa-envelope text-muted fa-stack-1x"></span>
|
||||
<span class="fa fa-ban text-warning fa-stack-1x fa-stack-shifted"></span>
|
||||
</span>
|
||||
{% elif i.transmission_status == "failed" %}
|
||||
<span class="fa-stack fa-stack-small" data-toggle="tooltip" title="{% trans "Invoice transmission failed" %}">
|
||||
<span class="fa fa-background fa-envelope text-muted fa-stack-1x"></span>
|
||||
<span class="fa fa-exclamation-circle text-danger fa-stack-1x fa-stack-shifted"></span>
|
||||
</span>
|
||||
{% elif i.transmission_status == "completed" %}
|
||||
<span class="fa-stack fa-stack-small" data-toggle="tooltip" title="{% trans "Invoice has been transmitted" %}">
|
||||
<span class="fa fa-background fa-envelope text-muted fa-stack-1x"></span>
|
||||
<span class="fa fa-check text-success fa-stack-1x fa-stack-shifted"></span>
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="fa-stack fa-stack-small" data-toggle="tooltip" title="{% trans "Invoice was not yet emailed to customer" %}">
|
||||
<span class="fa fa-background fa-envelope text-muted fa-stack-1x"></span>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if i.transmission_status != "inflight" %}
|
||||
<form class="form-inline helper-display-inline" method="post"
|
||||
action="{% url "control:event.order.retransmitinvoice" event=request.event.slug organizer=request.event.organizer.slug code=order.code id=i.pk %}">
|
||||
{% csrf_token %}
|
||||
<button class="btn btn-default btn-xs" data-toggle="tooltip"
|
||||
title="{{ i.transmission_type_instance.verbose_name }}">
|
||||
{% if i.transmission_status == "pending" %}
|
||||
{% trans "Transmit" %}
|
||||
{% else %}
|
||||
{% trans "Retransmit" %}
|
||||
{% endif %}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if not i.canceled %}
|
||||
{% if request.event.settings.invoice_regenerate_allowed %}
|
||||
{% if i.regenerate_allowed %}
|
||||
<form class="form-inline helper-display-inline" method="post"
|
||||
action="{% url "control:event.order.regeninvoice" event=request.event.slug organizer=request.event.organizer.slug code=order.code id=i.pk %}">
|
||||
{% csrf_token %}
|
||||
@@ -320,12 +366,6 @@
|
||||
<br/>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if invoices_send_link %}
|
||||
<br/>
|
||||
<a class="btn btn-default btn-xs" href="{{ invoices_send_link }}">
|
||||
{% trans "Email invoices" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if can_generate_invoice and 'can_change_orders' in request.eventpermset %}
|
||||
<br/>
|
||||
<form class="form-inline helper-display-inline" method="post"
|
||||
@@ -989,8 +1029,14 @@
|
||||
<dt>{{ request.event.settings.invoice_address_custom_field }}</dt>
|
||||
<dd>{{ order.invoice_address.custom_field }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Internal reference" %}</dt>
|
||||
<dd>{{ order.invoice_address.internal_reference }}</dd>
|
||||
{% if order.invoice_address.internal_reference %}
|
||||
<dt>{% trans "Internal reference" %}</dt>
|
||||
<dd>{{ order.invoice_address.internal_reference }}</dd>
|
||||
{% endif %}
|
||||
{% for k, v in order.invoice_address.describe_transmission %}
|
||||
<dt>{{ k }}</dt>
|
||||
<dd>{{ v }}</dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -383,6 +383,8 @@ urlpatterns = [
|
||||
name='event.order.geninvoice'),
|
||||
re_path(r'^orders/(?P<code>[0-9A-Z]+)/invoices/(?P<id>\d+)/regenerate$', orders.OrderInvoiceRegenerate.as_view(),
|
||||
name='event.order.regeninvoice'),
|
||||
re_path(r'^orders/(?P<code>[0-9A-Z]+)/invoices/(?P<id>\d+)/retransmit$', orders.OrderInvoiceRetransmit.as_view(),
|
||||
name='event.order.retransmitinvoice'),
|
||||
re_path(r'^orders/(?P<code>[0-9A-Z]+)/invoices/(?P<id>\d+)/reissue$', orders.OrderInvoiceReissue.as_view(),
|
||||
name='event.order.reissueinvoice'),
|
||||
re_path(r'^orders/(?P<code>[0-9A-Z]+)/download/(?P<position>\d+)/(?P<output>[^/]+)/$',
|
||||
|
||||
@@ -37,7 +37,7 @@ import json
|
||||
import logging
|
||||
import operator
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
from collections import OrderedDict, defaultdict
|
||||
from decimal import Decimal
|
||||
from io import BytesIO
|
||||
from itertools import groupby
|
||||
@@ -76,6 +76,9 @@ from i18nfield.utils import I18nJSONEncoder
|
||||
|
||||
from pretix.base.email import get_available_placeholders
|
||||
from pretix.base.forms import PlaceholderValidator
|
||||
from pretix.base.invoicing.transmission import (
|
||||
get_transmission_types, transmission_providers,
|
||||
)
|
||||
from pretix.base.models import Event, LogEntry, Order, TaxRule, Voucher
|
||||
from pretix.base.models.event import EventMetaValue
|
||||
from pretix.base.services import tickets
|
||||
@@ -651,6 +654,22 @@ class InvoiceSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
template_name = 'pretixcontrol/event/invoicing.html'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
types = get_transmission_types()
|
||||
providers = defaultdict(list)
|
||||
ready = defaultdict(lambda: False)
|
||||
for p, __ in transmission_providers.filter(active_in=self.request.event):
|
||||
is_ready_result = p.is_ready(self.request.event)
|
||||
providers[p.type].append((p, is_ready_result, p.settings_url(self.request.event)))
|
||||
ready[p.type] = ready[p.type] or is_ready_result
|
||||
for k, v in providers.items():
|
||||
v.sort(key=lambda p: (-p[0].priority, p[0].identifier))
|
||||
return super().get_context_data(
|
||||
transmission_providers=providers,
|
||||
transmission_types=types,
|
||||
ready=ready,
|
||||
)
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
if 'preview' in self.request.POST:
|
||||
return reverse('control:event.settings.invoice.preview', kwargs={
|
||||
|
||||
@@ -66,7 +66,7 @@ from django.utils.html import conditional_escape, escape
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.timezone import make_aware, now
|
||||
from django.utils.translation import gettext, gettext_lazy as _, ngettext
|
||||
from django.utils.translation import gettext, gettext_lazy as _
|
||||
from django.views.generic import (
|
||||
DetailView, FormView, ListView, TemplateView, View,
|
||||
)
|
||||
@@ -93,7 +93,7 @@ from pretix.base.services.cancelevent import cancel_event
|
||||
from pretix.base.services.export import export, scheduled_event_export
|
||||
from pretix.base.services.invoices import (
|
||||
generate_cancellation, generate_invoice, invoice_pdf, invoice_pdf_task,
|
||||
invoice_qualified, regenerate_invoice,
|
||||
invoice_qualified, regenerate_invoice, transmit_invoice,
|
||||
)
|
||||
from pretix.base.services.locking import LockTimeoutException
|
||||
from pretix.base.services.mail import (
|
||||
@@ -551,27 +551,6 @@ class OrderDetail(OrderView):
|
||||
ctx['payment_refund_sum'] = self.order.payment_refund_sum
|
||||
ctx['pending_sum'] = self.order.pending_sum
|
||||
|
||||
unsent_invoices = [ii.pk for ii in ctx['invoices'] if not ii.sent_to_customer]
|
||||
if unsent_invoices:
|
||||
with language(self.order.locale):
|
||||
ctx['invoices_send_link'] = reverse('control:event.order.sendmail', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'code': self.order.code
|
||||
}) + '?' + urlencode({
|
||||
'subject': ngettext('Your invoice', 'Your invoices', len(unsent_invoices)),
|
||||
'message': ngettext(
|
||||
'Hello,\n\nplease find your invoice attached to this email.\n\n'
|
||||
'Your {event} team',
|
||||
'Hello,\n\nplease find your invoices attached to this email.\n\n'
|
||||
'Your {event} team',
|
||||
len(unsent_invoices)
|
||||
).format(
|
||||
event="{event}",
|
||||
),
|
||||
'attach_invoices': unsent_invoices
|
||||
}, doseq=True)
|
||||
|
||||
return ctx
|
||||
|
||||
@cached_property
|
||||
@@ -1681,6 +1660,8 @@ class OrderInvoiceRegenerate(OrderView):
|
||||
else:
|
||||
if not inv.event.settings.invoice_regenerate_allowed:
|
||||
messages.error(self.request, _('Invoices may not be changed after they are created.'))
|
||||
elif not inv.regenerate_allowed:
|
||||
messages.error(self.request, _('Invoices may not be changed after they are transmitted.'))
|
||||
if inv.canceled:
|
||||
messages.error(self.request, _('The invoice has already been canceled.'))
|
||||
elif inv.sent_to_organizer:
|
||||
@@ -1701,6 +1682,37 @@ class OrderInvoiceRegenerate(OrderView):
|
||||
return HttpResponseNotAllowed(['POST'])
|
||||
|
||||
|
||||
class OrderInvoiceRetransmit(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
with transaction.atomic(durable=True):
|
||||
try:
|
||||
invoice = self.order.invoices.select_for_update(of=OF_SELF).get(pk=kwargs.get("id"))
|
||||
except Invoice.DoesNotExist:
|
||||
messages.error(self.request, _('Unknown invoice.'))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
if invoice.transmission_status == Invoice.TRANSMISSION_STATUS_INFLIGHT:
|
||||
messages.error(self.request, _('The invoice is currently being transmitted. You can start a new attempt after '
|
||||
'the current one has been completed.'))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
invoice.transmission_status = Invoice.TRANSMISSION_STATUS_PENDING
|
||||
invoice.transmission_date = now()
|
||||
invoice.save(update_fields=["transmission_status", "transmission_date"])
|
||||
messages.success(self.request, _('The invoice has been scheduled for retransmission.'))
|
||||
self.order.log_action('pretix.event.order.invoice.retransmitted', user=self.request.user, data={
|
||||
'invoice': invoice.pk,
|
||||
'full_invoice_no': invoice.full_invoice_no,
|
||||
})
|
||||
transmit_invoice.apply_async(args=(self.request.event.pk, invoice.pk, True))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
def get(self, *args, **kwargs): # NOQA
|
||||
return HttpResponseNotAllowed(['POST'])
|
||||
|
||||
|
||||
class OrderInvoiceReissue(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user