mirror of
https://github.com/pretix/pretix.git
synced 2026-08-29 13:44:40 +00:00
Refs #131 -- Allow to regenerate an invoice
This commit is contained in:
@@ -5,6 +5,7 @@ from decimal import Decimal
|
|||||||
|
|
||||||
from django.db import DatabaseError, models
|
from django.db import DatabaseError, models
|
||||||
from django.db.models import Max
|
from django.db.models import Max
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
|
||||||
def invoice_filename(instance, filename: str) -> str:
|
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)
|
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:
|
class Meta:
|
||||||
unique_together = ('event', 'invoice_no')
|
unique_together = ('event', 'invoice_no')
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,56 @@ def generate_cancellation(invoice: Invoice):
|
|||||||
return cancellation
|
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
|
@transaction.atomic
|
||||||
def generate_invoice(order: Order):
|
def generate_invoice(order: Order):
|
||||||
locale = order.event.settings.get('invoice_language')
|
locale = order.event.settings.get('invoice_language')
|
||||||
|
|||||||
@@ -105,6 +105,15 @@
|
|||||||
<a href="{% url "control:event.invoice.download" invoice=i.pk event=request.event.slug organizer=request.event.organizer.slug %}">
|
<a href="{% url "control:event.invoice.download" invoice=i.pk event=request.event.slug organizer=request.event.organizer.slug %}">
|
||||||
{% if i.is_cancellation %}{% trans "Cancellation" %}{% else %}{% trans "Invoice" %}{% endif %}
|
{% if i.is_cancellation %}{% trans "Cancellation" %}{% else %}{% trans "Invoice" %}{% endif %}
|
||||||
{{ i.number }}</a> ({{ i.date|date:"SHORT_DATE_FORMAT" }})
|
{{ i.number }}</a> ({{ i.date|date:"SHORT_DATE_FORMAT" }})
|
||||||
|
{% if not i.canceled %}
|
||||||
|
<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 %}
|
||||||
|
<button class="btn btn-default btn-xs">
|
||||||
|
{% trans "Regenerate invoice" %}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
{% if forloop.revcounter0 > 0 %}
|
{% if forloop.revcounter0 > 0 %}
|
||||||
<br/>
|
<br/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ urlpatterns = [
|
|||||||
name='event.order.resendlink'),
|
name='event.order.resendlink'),
|
||||||
url(r'^orders/(?P<code>[0-9A-Z]+)/invoice$', orders.OrderInvoiceCreate.as_view(),
|
url(r'^orders/(?P<code>[0-9A-Z]+)/invoice$', orders.OrderInvoiceCreate.as_view(),
|
||||||
name='event.order.geninvoice'),
|
name='event.order.geninvoice'),
|
||||||
|
url(r'^orders/(?P<code>[0-9A-Z]+)/invoices/(?P<id>\d+)/regenerate$', orders.OrderInvoiceRegenerate.as_view(),
|
||||||
|
name='event.order.regeninvoice'),
|
||||||
url(r'^orders/(?P<code>[0-9A-Z]+)/extend$', orders.OrderExtend.as_view(),
|
url(r'^orders/(?P<code>[0-9A-Z]+)/extend$', orders.OrderExtend.as_view(),
|
||||||
name='event.order.extend'),
|
name='event.order.extend'),
|
||||||
url(r'^orders/(?P<code>[0-9A-Z]+)/$', orders.OrderDetail.as_view(), name='event.order'),
|
url(r'^orders/(?P<code>[0-9A-Z]+)/$', orders.OrderDetail.as_view(), name='event.order'),
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from pretix.base.models import (
|
|||||||
from pretix.base.services import tickets
|
from pretix.base.services import tickets
|
||||||
from pretix.base.services.export import export
|
from pretix.base.services.export import export
|
||||||
from pretix.base.services.invoices import (
|
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.mail import mail
|
||||||
from pretix.base.services.orders import cancel_order, mark_order_paid
|
from pretix.base.services.orders import cancel_order, mark_order_paid
|
||||||
@@ -228,8 +228,10 @@ class OrderInvoiceCreate(OrderView):
|
|||||||
elif self.order.invoices.exists():
|
elif self.order.invoices.exists():
|
||||||
messages.error(self.request, _('An invoice for this order already exists.'))
|
messages.error(self.request, _('An invoice for this order already exists.'))
|
||||||
else:
|
else:
|
||||||
generate_invoice(self.order)
|
inv = generate_invoice(self.order)
|
||||||
self.order.log_action('pretix.event.order.invoice.generate', user=self.request.user)
|
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.'))
|
messages.success(self.request, _('The invoice has been generated.'))
|
||||||
return redirect(self.get_order_url())
|
return redirect(self.get_order_url())
|
||||||
|
|
||||||
@@ -237,6 +239,26 @@ class OrderInvoiceCreate(OrderView):
|
|||||||
return HttpResponseNotAllowed(['POST'])
|
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):
|
class OrderResendLink(OrderView):
|
||||||
permission = 'can_change_orders'
|
permission = 'can_change_orders'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user