diff --git a/src/pretix/base/models/invoices.py b/src/pretix/base/models/invoices.py index 49288affa5..edb762bd5d 100644 --- a/src/pretix/base/models/invoices.py +++ b/src/pretix/base/models/invoices.py @@ -5,6 +5,7 @@ from decimal import Decimal from django.db import DatabaseError, models from django.db.models import Max +from django.utils.functional import cached_property def invoice_filename(instance, filename: str) -> str: @@ -80,6 +81,10 @@ class Invoice(models.Model): """ return '%s-%05d' % (self.event.slug.upper(), self.invoice_no) + @cached_property + def canceled(self): + return self.refered.filter(is_cancellation=True).exists() + class Meta: unique_together = ('event', 'invoice_no') diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index b2553f4db1..6fde414665 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -45,6 +45,56 @@ def generate_cancellation(invoice: Invoice): return cancellation +@transaction.atomic +def regenerate_invoice(invoice: Invoice): + with language(invoice.locale): + invoice.invoice_from = invoice.event.settings.get('invoice_address_from') + invoice.additional_text = invoice.event.settings.get('invoice_additional_text') + + try: + addr_template = pgettext("invoice", """{i.company} +{i.name} +{i.street} +{i.zipcode} {i.city} +{i.country}""") + invoice.invoice_to = addr_template.format(i=invoice.order.invoice_address).strip() + if invoice.order.invoice_address.vat_id: + invoice.invoice_to += "\n" + pgettext("invoice", "VAT-ID: %s") % invoice.order.invoice_address.vat_id + except InvoiceAddress.DoesNotExist: + invoice.invoice_to = "" + + invoice.file = None + invoice.save() + + responses = register_payment_providers.send(invoice.event) + for receiver, response in responses: + provider = response(invoice.event) + if provider.identifier == invoice.order.payment_provider: + payment_provider = provider + break + + invoice.lines.all().delete() + for p in invoice.order.positions.all(): + desc = str(p.item.name) + if p.variation: + desc += " - " + str(p.variation.value) + InvoiceLine.objects.create( + invoice=invoice, description=desc, + gross_value=p.price, tax_value=p.tax_value, + tax_rate=p.tax_rate + ) + + if invoice.order.payment_fee: + InvoiceLine.objects.create( + invoice=invoice, description=_('Payment via {method}').format(method=str(payment_provider.verbose_name)), + gross_value=invoice.order.payment_fee, tax_value=invoice.order.payment_fee_tax_value, + tax_rate=invoice.order.payment_fee_tax_rate + ) + + invoice_pdf(invoice.pk) + return invoice + + @transaction.atomic def generate_invoice(order: Order): locale = order.event.settings.get('invoice_language') diff --git a/src/pretix/control/templates/pretixcontrol/order/index.html b/src/pretix/control/templates/pretixcontrol/order/index.html index da137ac199..3830bfefed 100644 --- a/src/pretix/control/templates/pretixcontrol/order/index.html +++ b/src/pretix/control/templates/pretixcontrol/order/index.html @@ -105,6 +105,15 @@ {% if i.is_cancellation %}{% trans "Cancellation" %}{% else %}{% trans "Invoice" %}{% endif %} {{ i.number }} ({{ i.date|date:"SHORT_DATE_FORMAT" }}) + {% if not i.canceled %} +
+ {% endif %} {% if forloop.revcounter0 > 0 %}[0-9A-Z]+)/invoice$', orders.OrderInvoiceCreate.as_view(),
name='event.order.geninvoice'),
+ url(r'^orders/(?P[0-9A-Z]+)/invoices/(?P\d+)/regenerate$', orders.OrderInvoiceRegenerate.as_view(),
+ name='event.order.regeninvoice'),
url(r'^orders/(?P[0-9A-Z]+)/extend$', orders.OrderExtend.as_view(),
name='event.order.extend'),
url(r'^orders/(?P[0-9A-Z]+)/$', orders.OrderDetail.as_view(), name='event.order'),
diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py
index d849ab5e73..53c9acd82c 100644
--- a/src/pretix/control/views/orders.py
+++ b/src/pretix/control/views/orders.py
@@ -18,7 +18,7 @@ from pretix.base.models import (
from pretix.base.services import tickets
from pretix.base.services.export import export
from pretix.base.services.invoices import (
- generate_invoice, invoice_pdf, invoice_qualified,
+ generate_invoice, invoice_pdf, invoice_qualified, regenerate_invoice,
)
from pretix.base.services.mail import mail
from pretix.base.services.orders import cancel_order, mark_order_paid
@@ -228,8 +228,10 @@ class OrderInvoiceCreate(OrderView):
elif self.order.invoices.exists():
messages.error(self.request, _('An invoice for this order already exists.'))
else:
- generate_invoice(self.order)
- self.order.log_action('pretix.event.order.invoice.generate', user=self.request.user)
+ inv = generate_invoice(self.order)
+ self.order.log_action('pretix.event.order.invoice.generate', user=self.request.user, data={
+ 'invoice': inv.pk
+ })
messages.success(self.request, _('The invoice has been generated.'))
return redirect(self.get_order_url())
@@ -237,6 +239,26 @@ class OrderInvoiceCreate(OrderView):
return HttpResponseNotAllowed(['POST'])
+class OrderInvoiceRegenerate(OrderView):
+ permission = 'can_change_orders'
+
+ def post(self, *args, **kwargs):
+ try:
+ inv = self.order.invoices.get(pk=kwargs.get('id'))
+ except Order.DoesNotExist:
+ messages.error(self.request, _('Unknown invoice.'))
+
+ inv = regenerate_invoice(inv)
+ self.order.log_action('pretix.event.order.invoice.regenerate', user=self.request.user, data={
+ 'invoice': inv.pk
+ })
+ messages.success(self.request, _('The invoice has been regenerated.'))
+ return redirect(self.get_order_url())
+
+ def get(self, *args, **kwargs):
+ return HttpResponseNotAllowed(['POST'])
+
+
class OrderResendLink(OrderView):
permission = 'can_change_orders'