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 %} +
+ {% csrf_token %} + +
+ {% endif %} {% if forloop.revcounter0 > 0 %}
{% endif %} diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index 8715274b39..6205f565c6 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -71,6 +71,8 @@ urlpatterns = [ name='event.order.resendlink'), url(r'^orders/(?P[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'