diff --git a/src/db.sqlite3.bak b/src/db.sqlite3.bak new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/pretix/base/migrations/0012_auto_20160312_1040.py b/src/pretix/base/migrations/0012_auto_20160312_1040.py new file mode 100644 index 0000000000..58f916feeb --- /dev/null +++ b/src/pretix/base/migrations/0012_auto_20160312_1040.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-03-12 10:40 +from __future__ import unicode_literals + +import datetime +from decimal import Decimal + +import django.db.models.deletion +from django.db import migrations, models + +import pretix.base.models.invoices + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0011_auto_20160311_2052'), + ] + + operations = [ + migrations.CreateModel( + name='Invoice', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('invoice_no', models.PositiveIntegerField(db_index=True)), + ('is_cancelled', models.BooleanField(default=False)), + ('invoice_from', models.TextField()), + ('invoice_to', models.TextField()), + ('date', models.DateField(default=datetime.date.today)), + ('file', models.FileField(blank=True, null=True, upload_to=pretix.base.models.invoices.invoice_filename)), + ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invoices', to='pretixbase.Event')), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invoices', to='pretixbase.Order')), + ], + ), + migrations.CreateModel( + name='InvoiceLine', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('description', models.TextField()), + ('gross_value', models.DecimalField(decimal_places=2, max_digits=10)), + ('tax_value', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10)), + ('tax_rate', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=7)), + ('invoice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='pretixbase.Invoice')), + ], + ), + migrations.AlterUniqueTogether( + name='invoice', + unique_together=set([('event', 'invoice_no')]), + ), + ] diff --git a/src/pretix/base/migrations/0013_invoice_locale.py b/src/pretix/base/migrations/0013_invoice_locale.py new file mode 100644 index 0000000000..e321e29343 --- /dev/null +++ b/src/pretix/base/migrations/0013_invoice_locale.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-03-12 10:51 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0012_auto_20160312_1040'), + ] + + operations = [ + migrations.AddField( + model_name='invoice', + name='locale', + field=models.CharField(default='en', max_length=50), + ), + ] diff --git a/src/pretix/base/migrations/0014_invoice_additional_text.py b/src/pretix/base/migrations/0014_invoice_additional_text.py new file mode 100644 index 0000000000..594e458415 --- /dev/null +++ b/src/pretix/base/migrations/0014_invoice_additional_text.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-03-12 11:07 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0013_invoice_locale'), + ] + + operations = [ + migrations.AddField( + model_name='invoice', + name='additional_text', + field=models.TextField(blank=True), + ), + ] diff --git a/src/pretix/base/migrations/0015_auto_20160312_1924.py b/src/pretix/base/migrations/0015_auto_20160312_1924.py new file mode 100644 index 0000000000..4eb137c26c --- /dev/null +++ b/src/pretix/base/migrations/0015_auto_20160312_1924.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-03-12 19:24 +from __future__ import unicode_literals + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0014_invoice_additional_text'), + ] + + operations = [ + migrations.RemoveField( + model_name='invoice', + name='is_cancelled', + ), + migrations.AddField( + model_name='invoice', + name='is_cancellation', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='invoice', + name='refers', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='refered', to='pretixbase.Invoice'), + ), + ] diff --git a/src/pretix/base/models/__init__.py b/src/pretix/base/models/__init__.py index 7a0db98b34..18981e26a3 100644 --- a/src/pretix/base/models/__init__.py +++ b/src/pretix/base/models/__init__.py @@ -1,6 +1,7 @@ from .auth import User from .base import CachedFile, cachedfile_name from .event import Event, EventLock, EventPermission, EventSetting +from .invoices import Invoice, InvoiceLine, invoice_filename from .items import ( Item, ItemCategory, ItemVariation, Question, Quota, itempicture_upload_to, ) @@ -17,5 +18,6 @@ __all__ = [ 'ItemCategory', 'Item', 'Property', 'PropertyValue', 'ItemVariation', 'VariationsField', 'Question', 'BaseRestriction', 'Quota', 'Order', 'CachedTicket', 'QuestionAnswer', 'AbstractPosition', 'OrderPosition', 'CartPosition', 'EventSetting', 'OrganizerSetting', 'EventLock', 'cachedfile_name', 'itempicture_upload_to', - 'generate_secret', 'Voucher', 'LogEntry', 'InvoiceAddress', 'generate_position_secret' + 'generate_secret', 'Voucher', 'LogEntry', 'InvoiceAddress', 'generate_position_secret', 'InvoiceLine', + 'Invoice', 'invoice_filename' ] diff --git a/src/pretix/base/models/invoices.py b/src/pretix/base/models/invoices.py new file mode 100644 index 0000000000..9bb6ea52f8 --- /dev/null +++ b/src/pretix/base/models/invoices.py @@ -0,0 +1,62 @@ +import random +import string +from datetime import date +from decimal import Decimal + +from django.db import DatabaseError, models +from django.db.models import Max + + +def invoice_filename(instance, filename: str) -> str: + secret = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(14)) + return 'invoices/{org}/{ev}/{ev}-{no}-{code}-{secret}.pdf'.format( + org=instance.event.organizer.slug, ev=instance.event.slug, + no=instance.invoice_no, code=instance.order.code, + secret=secret + ) + + +class Invoice(models.Model): + order = models.ForeignKey('Order', related_name='invoices', db_index=True) + event = models.ForeignKey('Event', related_name='invoices', db_index=True) + invoice_no = models.PositiveIntegerField(db_index=True) + is_cancellation = models.BooleanField(default=False) + refers = models.ForeignKey('Invoice', related_name='refered', null=True, blank=True) + invoice_from = models.TextField() + invoice_to = models.TextField() + date = models.DateField(default=date.today) + locale = models.CharField(max_length=50, default='en') + additional_text = models.TextField(blank=True) + file = models.FileField(null=True, blank=True, upload_to=invoice_filename) + + def save(self, *args, **kwargs): + if not self.order: + raise ValueError('Any invoice needs to be connected to an order') + if not self.event: + self.event = self.order.event + if not self.invoice_no: + for i in range(10): + self.invoice_no = (Invoice.objects.filter( + event=self.event).aggregate(m=Max('invoice_no'))['m'] or 0) + 1 + try: + return super().save(*args, **kwargs) + except DatabaseError: + # Suppress duplicate key errors and try again + if i == 9: + raise + return super().save(*args, **kwargs) + + @property + def number(self): + return '%s-%05d' % (self.event.slug.upper(), self.invoice_no) + + class Meta: + unique_together = ('event', 'invoice_no') + + +class InvoiceLine(models.Model): + invoice = models.ForeignKey('Invoice', related_name='lines') + description = models.TextField() + gross_value = models.DecimalField(max_digits=10, decimal_places=2) + tax_value = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal('0.00')) + tax_rate = models.DecimalField(max_digits=7, decimal_places=2, default=Decimal('0.00')) diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py new file mode 100644 index 0000000000..5110893b81 --- /dev/null +++ b/src/pretix/base/services/invoices.py @@ -0,0 +1,338 @@ +import copy +import tempfile +from collections import defaultdict +from datetime import date +from decimal import Decimal +from locale import format as lformat + +from django.conf import settings +from django.contrib.staticfiles import finders +from django.core.files.base import ContentFile +from django.db import transaction +from django.utils import translation +from django.utils.formats import date_format +from django.utils.translation import pgettext, ugettext as _ +from reportlab.lib import pagesizes +from reportlab.lib.styles import ParagraphStyle, StyleSheet1 +from reportlab.lib.units import mm +from reportlab.pdfbase import pdfmetrics +from reportlab.pdfbase.ttfonts import TTFont +from reportlab.platypus import ( + BaseDocTemplate, Frame, NextPageTemplate, PageTemplate, Paragraph, Spacer, + Table, TableStyle, +) + +from pretix.base.models import Invoice, InvoiceAddress, InvoiceLine, Order +from pretix.base.signals import register_payment_providers + + +def generate_cancellation(invoice: Invoice): + cancellation = copy.copy(invoice) + cancellation.pk = None + cancellation.is_cancellation = True + cancellation.date = date.today() + cancellation.refers = invoice + cancellation.invoice_no = None + cancellation.save() + for line in invoice.lines.all(): + line.pk = None + line.invoice = cancellation + line.gross_value *= -1 + line.tax_value *= -1 + line.save() + + invoice_pdf(cancellation.pk) + + +@transaction.atomic +def generate_invoice(order: Order): + locale = order.event.settings.get('invoice_language') + _lng = translation.get_language() + if locale: + if locale == '__user__': + locale = order.locale + translation.activate(locale or settings.LANGUAGE_CODE) + + i = Invoice(order=order, event=order.event) + i.invoice_from = order.event.settings.get('invoice_address_from') + i.additional_text = order.event.settings.get('invoice_additional_text') + + try: + addr_template = pgettext("invoice", """{i.company} +{i.name} +{i.street} +{i.zipcode} {i.city} +{i.country}""") + i.invoice_to = addr_template.format(i=order.invoice_address).strip() + if order.invoice_address.vat_id: + i.invoice_to += "\n" + pgettext("invoice", "VAT-ID: %s") % {i.vat_id} + except InvoiceAddress.DoesNotExist: + i.invoice_to = "" + + i.date = date.today() + i.locale = locale + i.save() + + responses = register_payment_providers.send(order.event) + for receiver, response in responses: + provider = response(order.event) + if provider.identifier == order.payment_provider: + payment_provider = provider + break + + for p in order.positions.all(): + desc = str(p.item.name) + if p.variation: + desc += " - " + str(p.variation.value) + InvoiceLine.objects.create( + invoice=i, description=desc, + gross_value=p.price, tax_value=p.tax_value, + tax_rate=p.tax_rate + ) + + if order.payment_fee: + InvoiceLine.objects.create( + invoice=i, description=_('Payment via {method}').format(method=str(payment_provider.verbose_name)), + gross_value=order.payment_fee, tax_value=order.payment_fee_tax_value, + tax_rate=order.payment_fee_tax_rate + ) + + translation.activate(_lng) + invoice_pdf(i.pk) + + +def _invoice_get_stylesheet(): + stylesheet = StyleSheet1() + stylesheet.add(ParagraphStyle(name='Normal', fontName='OpenSans', fontSize=10, leading=12)) + stylesheet.add(ParagraphStyle(name='Heading1', fontName='OpenSansBd', fontSize=15, leading=15 * 1.2)) + return stylesheet + + +def _invoice_register_fonts(): + pdfmetrics.registerFont(TTFont('OpenSans', finders.find('fonts/OpenSans-Regular.ttf'))) + pdfmetrics.registerFont(TTFont('OpenSansIt', finders.find('fonts/OpenSans-Italic.ttf'))) + pdfmetrics.registerFont(TTFont('OpenSansBd', finders.find('fonts/OpenSans-Bold.ttf'))) + + +def _invoice_generate_german(invoice, f): + _invoice_register_fonts() + styles = _invoice_get_stylesheet() + pagesize = pagesizes.A4 + + def on_page(canvas, doc): + canvas.saveState() + canvas.setFont('OpenSans', 8) + canvas.drawRightString(pagesize[0] - 20 * mm, 10 * mm, _("Page %d") % (doc.page,)) + canvas.restoreState() + + def on_first_page(canvas, doc): + canvas.setCreator('pretix.eu') + canvas.setTitle(pgettext('invoice', 'Invoice {num}').format(num=invoice.number)) + + canvas.saveState() + canvas.setFont('OpenSans', 8) + canvas.drawRightString(pagesize[0] - 20 * mm, 10 * mm, _("Page %d") % (doc.page,)) + + textobject = canvas.beginText(25 * mm, (297 - 15) * mm) + textobject.setFont('OpenSansBd', 8) + textobject.textLine(pgettext('invoice', 'Invoice from').upper()) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSans', 10) + textobject.textLines(invoice.invoice_from.strip()) + canvas.drawText(textobject) + + textobject = canvas.beginText(25 * mm, (297 - 50) * mm) + textobject.setFont('OpenSansBd', 8) + textobject.textLine(pgettext('invoice', 'Invoice to').upper()) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSans', 10) + textobject.textLines(invoice.invoice_to.strip()) + canvas.drawText(textobject) + + textobject = canvas.beginText(125 * mm, (297 - 50) * mm) + textobject.setFont('OpenSansBd', 8) + if invoice.is_cancellation: + textobject.textLine(pgettext('invoice', 'Cancellation number').upper()) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSans', 10) + textobject.textLine(invoice.number) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSansBd', 8) + textobject.textLine(pgettext('invoice', 'Original invoice').upper()) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSans', 10) + textobject.textLine(invoice.refers.number) + else: + textobject.textLine(pgettext('invoice', 'Invoice number').upper()) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSans', 10) + textobject.textLine(invoice.number) + textobject.moveCursor(0, 5) + + if invoice.is_cancellation: + textobject.setFont('OpenSansBd', 8) + textobject.textLine(pgettext('invoice', 'Cancellation date').upper()) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSans', 10) + textobject.textLine(date_format(invoice.date, "DATE_FORMAT")) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSansBd', 8) + textobject.textLine(pgettext('invoice', 'Original invoice date').upper()) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSans', 10) + textobject.textLine(date_format(invoice.refers.date, "DATE_FORMAT")) + textobject.moveCursor(0, 5) + else: + textobject.setFont('OpenSansBd', 8) + textobject.textLine(pgettext('invoice', 'Invoice date').upper()) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSans', 10) + textobject.textLine(date_format(invoice.date, "DATE_FORMAT")) + textobject.moveCursor(0, 5) + + canvas.drawText(textobject) + + textobject = canvas.beginText(165 * mm, (297 - 50) * mm) + textobject.setFont('OpenSansBd', 8) + textobject.textLine(_('Order code').upper()) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSans', 10) + textobject.textLine(invoice.order.code) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSansBd', 8) + textobject.textLine(_('Order date').upper()) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSans', 10) + textobject.textLine(date_format(invoice.order.datetime, "DATE_FORMAT")) + canvas.drawText(textobject) + + textobject = canvas.beginText(125 * mm, (297 - 15) * mm) + textobject.setFont('OpenSansBd', 8) + textobject.textLine(_('Event').upper()) + textobject.moveCursor(0, 5) + textobject.setFont('OpenSans', 10) + textobject.textLine(str(invoice.event.name)) + if invoice.event.settings.show_date_to: + textobject.textLines( + _('%s\nuntil %s') % (invoice.event.get_date_from_display(), + invoice.event.get_date_to_display())) + else: + textobject.textLine(invoice.event.get_date_from_display()) + canvas.drawText(textobject) + + canvas.restoreState() + + doc = BaseDocTemplate(f.name, pagesize=pagesizes.A4, + leftMargin=25 * mm, rightMargin=20 * mm, + topMargin=20 * mm, bottomMargin=15 * mm) + frames_p1 = [ + Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height - 75 * mm, + leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0, + id='normal') + ] + frames = [ + Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, + leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0, + id='normal') + ] + doc.addPageTemplates([ + PageTemplate(id='FirstPage', frames=frames_p1, onPage=on_first_page, pagesize=pagesize), + PageTemplate(id='OtherPages', frames=frames, onPage=on_page, pagesize=pagesize) + ]) + story = [ + NextPageTemplate('FirstPage'), + Paragraph(pgettext('invoice', 'Invoice') + if not invoice.is_cancellation + else pgettext('invoice', 'Cancellation'), + styles['Heading1']), + Spacer(1, 5 * mm), + NextPageTemplate('OtherPages'), + ] + + taxvalue_map = defaultdict(Decimal) + grossvalue_map = defaultdict(Decimal) + + tstyledata = [ + ('ALIGN', (1, 0), (-1, -1), 'RIGHT'), + ('FONTNAME', (0, 0), (-1, 0), 'OpenSansBd'), + ('FONTNAME', (0, -1), (-1, -1), 'OpenSansBd'), + ('LEFTPADDING', (0, 0), (0, -1), 0), + ('RIGHTPADDING', (-1, 0), (-1, -1), 0), + ] + tdata = [(pgettext('invoice', 'Description'), pgettext('invoice', 'Tax rate'), pgettext('invoice', 'Price'))] + total = Decimal('0.00') + for line in invoice.lines.all(): + tdata.append(( + line.description, + lformat("%.2f", line.tax_rate) + " %", + lformat("%.2f", line.gross_value) + " " + invoice.event.currency, + )) + taxvalue_map[line.tax_rate] += line.tax_value + grossvalue_map[line.tax_rate] += line.gross_value + total += line.gross_value + + tdata.append([pgettext('invoice', 'Invoice total'), '', lformat("%.2f", total) + " " + invoice.event.currency]) + colwidths = [a * doc.width for a in (.60, .20, .20)] + table = Table(tdata, colWidths=colwidths, repeatRows=1) + table.setStyle(TableStyle(tstyledata)) + story.append(table) + + story.append(Spacer(1, 15 * mm)) + story.append(Paragraph(invoice.additional_text, styles['Normal'])) + story.append(Spacer(1, 15 * mm)) + + tstyledata = [ + ('SPAN', (1, 0), (-1, 0)), + ('ALIGN', (2, 1), (-1, -1), 'RIGHT'), + ('LEFTPADDING', (0, 0), (0, -1), 0), + ('RIGHTPADDING', (-1, 0), (-1, -1), 0), + ('FONTSIZE', (0, 0), (-1, -1), 8), + ] + tdata = [('', pgettext('invoice', 'Included taxes'), '', '', ''), + ('', pgettext('invoice', 'Tax rate'), + pgettext('invoice', 'Net value'), pgettext('invoice', 'Gross value'), pgettext('invoice', 'Tax'))] + + for rate, gross in grossvalue_map.items(): + if line.tax_rate == 0: + continue + tax = taxvalue_map[rate] + tdata.append(( + '', + lformat("%.2f", rate) + " %", + lformat("%.2f", (gross - tax)) + " " + invoice.event.currency, + lformat("%.2f", gross) + " " + invoice.event.currency, + lformat("%.2f", tax) + " " + invoice.event.currency, + )) + + if len(tdata) > 2: + colwidths = [a * doc.width for a in (.45, .10, .15, .15, .15)] + table = Table(tdata, colWidths=colwidths, repeatRows=2) + table.setStyle(TableStyle(tstyledata)) + story.append(table) + + doc.build(story) + return doc + + +def invoice_pdf(invoice: int): + i = Invoice.objects.get(pk=invoice) + _lng = translation.get_language() + translation.activate(i.locale) + + with tempfile.NamedTemporaryFile(suffix=".pdf") as f: + _invoice_generate_german(i, f) + f.seek(0) + i.file.save('invoice.pdf', ContentFile(f.read())) + i.save() + + translation.activate(_lng) + return i.file.name + + +if settings.HAS_CELERY: + from pretix.celery import app + + invoice_pdf_task = app.task(invoice_pdf) + + def invoice_pdf(*args, **kwargs): + invoice_pdf_task.apply_async(args=args, kwargs=kwargs) diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index 25a8b1dfcf..609af75cb4 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -12,6 +12,9 @@ from pretix.base.models import ( ) from pretix.base.models.orders import InvoiceAddress from pretix.base.payment import BasePaymentProvider +from pretix.base.services.invoices import ( + generate_cancellation, generate_invoice, invoice_pdf, +) from pretix.base.services.mail import mail from pretix.base.signals import ( order_paid, order_placed, register_payment_providers, @@ -99,6 +102,11 @@ def mark_order_refunded(order: Order, user: User=None): order.status = Order.STATUS_REFUNDED order.save() order.log_action('pretix.event.order.refunded', user=user) + + i = order.invoices.filter(is_cancellation=False).last() + if i: + generate_cancellation(i) + return order @@ -112,6 +120,11 @@ def cancel_order(order: Order, user: User=None): order.status = Order.STATUS_CANCELLED order.save() order.log_action('pretix.event.order.cancelled', user=user) + + i = order.invoices.filter(is_cancellation=False).last() + if i: + generate_cancellation(i) + return order @@ -250,6 +263,9 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str], except InvoiceAddress.DoesNotExist: pass + if event.settings.get('invoice_generate'): + generate_invoice(order) + mail( order.email, _('Your order: %(code)s') % {'code': order.code}, event.settings.mail_text_order_placed, diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index ac7819d452..9a4e61bb16 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -61,6 +61,22 @@ DEFAULTS = { 'default': '0.00', 'type': decimal.Decimal }, + 'invoice_generate': { + 'default': 'False', + 'type': bool + }, + 'invoice_address_from': { + 'default': '', + 'type': str + }, + 'invoice_additional_text': { + 'default': '', + 'type': str + }, + 'invoice_language': { + 'default': '__user__', + 'type': str + }, 'show_items_outside_presale_period': { 'default': 'True', 'type': bool diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index e6df5a8292..e0060b91b9 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -148,6 +148,26 @@ class EventSettingsForm(SettingsForm): help_text=_("Does only work if an invoice address is asked for. VAT ID is not required."), required=False ) + invoice_generate = forms.BooleanField( + label=_("Generate invoices"), + required=False + ) + invoice_address_from = forms.CharField( + widget=forms.Textarea(attrs={'rows': 5}), required=False, + label=_("Your address"), + help_text=_("Will be printed as the sender on invoices. Be sure to include relevant details required in " + "your jurisdiction (e.g. your VAT ID).") + ) + invoice_additional_text = forms.CharField( + widget=forms.Textarea(attrs={'rows': 5}), required=False, + label=_("Additional text"), + help_text=_("Will be printed on every invoice below the invoice total.") + ) + invoice_language = forms.ChoiceField( + widget=forms.Select, required=True, + label=_("Invoice language"), + choices=[('__user__', _('The user\'s language'))] + settings.LANGUAGES, + ) max_items_per_order = forms.IntegerField( min_value=1, label=_("Maximum number of items per order") diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html index a9ae5e785c..773dc47eb8 100644 --- a/src/pretix/control/templates/pretixcontrol/event/settings.html +++ b/src/pretix/control/templates/pretixcontrol/event/settings.html @@ -48,6 +48,10 @@ {% bootstrap_field sform.invoice_address_required layout="horizontal" %} {% bootstrap_field sform.invoice_address_vatid layout="horizontal" %} {% bootstrap_field sform.tax_rate_default layout="horizontal" %} + {% bootstrap_field sform.invoice_generate layout="horizontal" %} + {% bootstrap_field sform.invoice_language layout="horizontal" %} + {% bootstrap_field sform.invoice_address_from layout="horizontal" %} + {% bootstrap_field sform.invoice_additional_text layout="horizontal" %}
+ {% if invoices %} +
{% trans "Invoices" %}
+
+ {% for i in invoices %} + + {% if i.is_cancellation %}{% trans "Cancellation" %}{% else %}{% trans "Invoice" %}{% endif %} + {{ i.number }} ({{ i.date|date:"SHORT_DATE_FORMAT" }}) + {% if forloop.revcounter0 > 0 %} +
+ {% endif %} + {% endfor %} +
+ {% endif %}
diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index 7a7e45c132..5f422729fb 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -70,6 +70,8 @@ urlpatterns = [ url(r'^orders/(?P[0-9A-Z]+)/$', orders.OrderDetail.as_view(), name='event.order'), url(r'^orders/(?P[0-9A-Z]+)/download/(?P[^/]+)$', orders.OrderDownload.as_view(), name='event.order.download'), + url(r'^invoice/(?P[^/]+)$', orders.InvoiceDownload.as_view(), + name='event.invoice.download'), url(r'^orders/overview/$', orders.OverView.as_view(), name='event.orders.overview'), url(r'^orders/export/$', orders.ExportView.as_view(), name='event.orders.export'), url(r'^orders/go$', orders.OrderGo.as_view(), name='event.orders.go'), diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py index 82b4693371..9ccc26f9c8 100644 --- a/src/pretix/control/views/orders.py +++ b/src/pretix/control/views/orders.py @@ -5,7 +5,7 @@ from django import forms from django.contrib import messages from django.core.urlresolvers import reverse from django.db.models import Q -from django.http import HttpResponse, HttpResponseNotAllowed +from django.http import Http404, HttpResponseNotAllowed from django.shortcuts import redirect, render from django.utils.functional import cached_property from django.utils.timezone import now @@ -13,10 +13,11 @@ from django.utils.translation import ugettext_lazy as _ from django.views.generic import DetailView, ListView, TemplateView, View from pretix.base.models import ( - CachedFile, CachedTicket, EventLock, Item, Order, Quota, + CachedFile, CachedTicket, EventLock, Invoice, Item, Order, Quota, ) from pretix.base.services import tickets from pretix.base.services.export import export +from pretix.base.services.invoices import invoice_pdf from pretix.base.services.mail import mail from pretix.base.services.orders import cancel_order, mark_order_paid from pretix.base.services.stats import order_overview @@ -118,6 +119,7 @@ class OrderDetail(OrderView): and self.order.status == Order.STATUS_PAID ) ctx['payment'] = self.payment_provider.order_control_render(self.request, self.object) + ctx['invoices'] = list(self.order.invoices.all()) return ctx def get_items(self): @@ -137,8 +139,8 @@ class OrderDetail(OrderView): def keyfunc(pos): if (pos.item.admission and self.request.event.settings.attendee_names_asked) \ or pos.item.questions.all(): - return pos.id, 0, 0, 0, None - return 0, pos.item_id, pos.variation_id, pos.price, pos.voucher + return pos.id, 0, 0, 0, 0, None + return 0, pos.item_id, pos.variation_id, pos.price, pos.tax_rate, pos.voucher positions = [] for k, g in groupby(sorted(list(cartpos), key=keyfunc), key=keyfunc): @@ -221,6 +223,38 @@ class OrderResendLink(OrderView): return redirect(self.get_order_url()) +class InvoiceDownload(EventPermissionRequiredMixin, View): + permission = 'can_view_orders' + + def get_order_url(self): + return reverse('control:event.order', kwargs={ + 'event': self.request.event.slug, + 'organizer': self.request.event.organizer.slug, + 'code': self.invoice.order.code + }) + + def get(self, request, *args, **kwargs): + try: + self.invoice = Invoice.objects.get( + event=self.request.event, + id=self.kwargs['invoice'] + ) + except Invoice.DoesNotExist: + raise Http404(_('This invoice has not been found')) + + if not self.invoice.file: + invoice_pdf(self.invoice.pk) + self.invoice = Invoice.objects.get(pk=self.invoice.pk) + + if not self.invoice.file: + # This happens if we have celery installed and the file will be generated in the background + messages.warning(request, _('The invoice file has not yet been generated, we will generate it for you ' + 'now. Please try again in a few seconds.')) + return redirect(self.get_order_url()) + + return redirect(self.invoice.file.url) + + class OrderDownload(OrderView): @cached_property diff --git a/src/pretix/locale/de/LC_MESSAGES/django.po b/src/pretix/locale/de/LC_MESSAGES/django.po index bb47d4faf1..96ba0e063b 100644 --- a/src/pretix/locale/de/LC_MESSAGES/django.po +++ b/src/pretix/locale/de/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-10 22:11+0000\n" -"PO-Revision-Date: 2016-03-10 23:12+0100\n" +"POT-Creation-Date: 2016-03-13 14:52+0000\n" +"PO-Revision-Date: 2016-03-13 15:58+0100\n" "Last-Translator: Raphael Michel \n" "Language-Team: Raphael Michel \n" "Language: de\n" @@ -28,12 +28,12 @@ msgstr "aktiviert" #: pretix/base/forms/auth.py:13 pretix/base/forms/auth.py:140 #: pretix/base/models/auth.py:63 pretix/base/models/orders.py:100 -#: pretix/presale/forms/checkout.py:10 +#: pretix/presale/forms/checkout.py:12 msgid "E-mail" msgstr "E-Mail" #: pretix/base/forms/auth.py:14 pretix/base/forms/auth.py:77 -#: pretix/base/forms/auth.py:113 pretix/control/forms/event.py:252 +#: pretix/base/forms/auth.py:113 pretix/control/forms/event.py:277 msgid "Password" msgstr "Passwort" @@ -149,10 +149,10 @@ msgid "%(family)s, %(given)s" msgstr "%(family)s, %(given)s" #: pretix/base/models/event.py:55 pretix/base/models/items.py:421 -#: pretix/base/models/orders.py:477 pretix/base/models/organizer.py:25 -#: pretix/control/templates/pretixcontrol/order/index.html:190 +#: pretix/base/models/orders.py:494 pretix/base/models/organizer.py:25 +#: pretix/control/templates/pretixcontrol/order/index.html:208 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:77 -#: pretix/presale/templates/pretixpresale/event/order.html:113 +#: pretix/presale/templates/pretixpresale/event/order.html:137 #: pretix/presale/templates/pretixpresale/organizers/index.html:21 msgid "Name" msgstr "Name" @@ -228,7 +228,8 @@ msgstr "Erweiterungen" #: pretix/base/models/event.py:99 pretix/base/models/items.py:109 #: pretix/base/models/items.py:417 pretix/base/models/orders.py:95 -#: pretix/base/models/orders.py:439 pretix/base/models/vouchers.py:25 +#: pretix/base/models/orders.py:456 pretix/base/models/vouchers.py:25 +#: pretix/base/services/invoices.py:211 msgid "Event" msgstr "Veranstaltung" @@ -446,7 +447,7 @@ msgstr "Gesamtanzahl" msgid "Leave empty for an unlimited number of tickets." msgstr "Leer lassen für unbegrenzt viele Tickets." -#: pretix/base/models/items.py:430 pretix/base/models/orders.py:299 +#: pretix/base/models/items.py:430 pretix/base/models/orders.py:317 msgid "Item" msgstr "Produkt" @@ -488,7 +489,7 @@ msgstr "storniert" msgid "refunded" msgstr "erstattet" -#: pretix/base/models/orders.py:86 +#: pretix/base/models/orders.py:86 pretix/base/services/invoices.py:197 #: pretix/control/templates/pretixcontrol/attendees/index.html:32 #: pretix/control/templates/pretixcontrol/order/index.html:58 #: pretix/control/templates/pretixcontrol/orders/index.html:10 @@ -508,14 +509,14 @@ msgstr "Status" msgid "Locale" msgstr "Sprache" -#: pretix/base/models/orders.py:108 pretix/base/models/orders.py:446 +#: pretix/base/models/orders.py:108 pretix/base/models/orders.py:463 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_assign.html:17 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_confirm.html:13 #: pretix/presale/templates/pretixpresale/organizers/index.html:22 msgid "Date" msgstr "Datum" -#: pretix/base/models/orders.py:111 pretix/base/models/orders.py:450 +#: pretix/base/models/orders.py:111 pretix/base/models/orders.py:467 #: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form.html:23 msgid "Expiration date" msgstr "Ablaufdatum" @@ -531,126 +532,135 @@ msgid "Payment provider" msgstr "Zahlungsmethode" #: pretix/base/models/orders.py:124 -#: pretix/control/templates/pretixcontrol/order/index.html:140 +#: pretix/control/templates/pretixcontrol/order/index.html:152 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:75 msgid "Payment method fee" msgstr "Gebühr für gewählte Zahlungsmethode" -#: pretix/base/models/orders.py:127 -#: pretix/control/templates/pretixcontrol/order/index.html:162 +#: pretix/base/models/orders.py:128 +msgid "Payment method fee tax rate" +msgstr "Steuersatz auf Gebühr für gewählte Zahlungsmethode" + +#: pretix/base/models/orders.py:132 +msgid "Payment method fee tax" +msgstr "Steuerbetrag auf Gebühr für gewählte Zahlungsmethode" + +#: pretix/base/models/orders.py:135 +#: pretix/control/templates/pretixcontrol/order/index.html:180 msgid "Payment information" msgstr "Zahlungsinformationen" -#: pretix/base/models/orders.py:131 +#: pretix/base/models/orders.py:139 msgid "Payment state was manually modified" msgstr "Der Bestellungsstatus wurde manuell verändert" -#: pretix/base/models/orders.py:136 +#: pretix/base/models/orders.py:144 msgid "Total amount" msgstr "Gesamtbetrag" -#: pretix/base/models/orders.py:140 pretix/base/models/orders.py:367 +#: pretix/base/models/orders.py:148 pretix/base/models/orders.py:385 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_confirm.html:17 msgid "Order" msgstr "Bestellung" -#: pretix/base/models/orders.py:141 +#: pretix/base/models/orders.py:149 #: pretix/control/templates/pretixcontrol/event/base.html:102 +#: pretix/control/templates/pretixcontrol/event/settings.html:39 #: pretix/control/templates/pretixcontrol/orders/index.html:3 #: pretix/control/templates/pretixcontrol/orders/index.html:5 msgid "Orders" msgstr "Bestellungen" -#: pretix/base/models/orders.py:190 +#: pretix/base/models/orders.py:208 msgid "The payment is too late to be accepted." msgstr "Die Zahlung kommt zu spät, um akzeptiert werden zu können." -#: pretix/base/models/orders.py:205 +#: pretix/base/models/orders.py:223 msgid "Some of the ordered products were no longer available." msgstr "Einige der ausgewählten Produkte sind nicht mehr verfügbar." -#: pretix/base/models/orders.py:305 +#: pretix/base/models/orders.py:323 msgid "Variation" msgstr "Variante" -#: pretix/base/models/orders.py:310 +#: pretix/base/models/orders.py:328 msgid "Price" msgstr "Preis" -#: pretix/base/models/orders.py:314 +#: pretix/base/models/orders.py:332 #: pretix/control/templates/pretixcontrol/attendees/index.html:34 -#: pretix/control/templates/pretixcontrol/order/index.html:106 -#: pretix/presale/forms/checkout.py:65 +#: pretix/control/templates/pretixcontrol/order/index.html:119 +#: pretix/presale/forms/checkout.py:67 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:16 msgid "Attendee name" msgstr "Name des Teilnehmers" -#: pretix/base/models/orders.py:316 +#: pretix/base/models/orders.py:334 msgid "Empty, if this product is not an admission ticket" msgstr "Leer, wenn dies kein Eintrittsticket ist" -#: pretix/base/models/orders.py:373 +#: pretix/base/models/orders.py:391 msgid "Tax rate" msgstr "Steuersatz" -#: pretix/base/models/orders.py:377 +#: pretix/base/models/orders.py:395 msgid "Tax value" msgstr "Steuer" -#: pretix/base/models/orders.py:382 +#: pretix/base/models/orders.py:400 msgid "Order position" msgstr "Bestelltes Produkt" -#: pretix/base/models/orders.py:383 +#: pretix/base/models/orders.py:401 msgid "Order positions" msgstr "Bestellzeile" -#: pretix/base/models/orders.py:443 +#: pretix/base/models/orders.py:460 msgid "Cart ID (e.g. session key)" msgstr "Warenkorb-ID (z.B. Session-ID)" -#: pretix/base/models/orders.py:454 +#: pretix/base/models/orders.py:471 msgid "Cart position" msgstr "Produkt im Warenkorb" -#: pretix/base/models/orders.py:455 +#: pretix/base/models/orders.py:472 msgid "Cart positions" msgstr "Produkte im Warenkorb" -#: pretix/base/models/orders.py:476 +#: pretix/base/models/orders.py:493 msgid "Company name" msgstr "Firmenname" -#: pretix/base/models/orders.py:478 -#: pretix/control/templates/pretixcontrol/order/index.html:192 +#: pretix/base/models/orders.py:495 +#: pretix/control/templates/pretixcontrol/order/index.html:210 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:79 -#: pretix/presale/templates/pretixpresale/event/order.html:115 +#: pretix/presale/templates/pretixpresale/event/order.html:139 msgid "Address" msgstr "Adresse" -#: pretix/base/models/orders.py:479 +#: pretix/base/models/orders.py:496 msgid "ZIP code" msgstr "Postleitzahl" -#: pretix/base/models/orders.py:480 +#: pretix/base/models/orders.py:497 msgid "City" msgstr "Ort" -#: pretix/base/models/orders.py:481 -#: pretix/control/templates/pretixcontrol/order/index.html:196 +#: pretix/base/models/orders.py:498 +#: pretix/control/templates/pretixcontrol/order/index.html:214 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:83 -#: pretix/presale/templates/pretixpresale/event/order.html:119 +#: pretix/presale/templates/pretixpresale/event/order.html:143 msgid "Country" msgstr "Land" -#: pretix/base/models/orders.py:482 +#: pretix/base/models/orders.py:499 msgid "Phone number" msgstr "Telefonnummer" -#: pretix/base/models/orders.py:483 -#: pretix/control/templates/pretixcontrol/order/index.html:201 +#: pretix/base/models/orders.py:500 +#: pretix/control/templates/pretixcontrol/order/index.html:219 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:88 -#: pretix/presale/templates/pretixpresale/event/order.html:124 +#: pretix/presale/templates/pretixpresale/event/order.html:148 msgid "VAT ID" msgstr "USt-ID" @@ -801,7 +811,7 @@ msgstr "Kostenlos" msgid "The order has been marked as refunded." msgstr "Die Bestellung wurde als zurückerstattet markiert." -#: pretix/base/services/cart.py:20 pretix/base/services/orders.py:30 +#: pretix/base/services/cart.py:20 pretix/base/services/orders.py:33 msgid "" "We were not able to process your request completely as the server was too " "busy. Please try again." @@ -817,7 +827,7 @@ msgstr "Sie haben keine Produkte ausgewählt." msgid "You selected a product which is not available for sale." msgstr "Sie haben ein Produkt ausgewählt, das nicht zum Verkauf steht." -#: pretix/base/services/cart.py:24 pretix/base/services/orders.py:22 +#: pretix/base/services/cart.py:24 pretix/base/services/orders.py:25 msgid "" "Some of the products you selected were no longer available. Please see below " "for details." @@ -825,7 +835,7 @@ msgstr "" "Einige der ausgewählten Produkte waren zwischenzeitlich nicht mehr " "verfügbar, bitte überprüfen Sie Ihren Warenkorb." -#: pretix/base/services/cart.py:26 pretix/base/services/orders.py:24 +#: pretix/base/services/cart.py:26 pretix/base/services/orders.py:27 msgid "" "Some of the products you selected were no longer available in the quantity " "you selected. Please see below for details." @@ -833,7 +843,7 @@ msgstr "" "Einige der ausgewählten Produkte waren zwischenzeitlich nicht mehr in der " "gewünschten Anzahl verfügbar, bitte überprüfen Sie Ihren Warenkorb." -#: pretix/base/services/cart.py:28 pretix/base/services/orders.py:28 +#: pretix/base/services/cart.py:28 pretix/base/services/orders.py:31 #, python-format msgid "You cannot select more than %s items per order" msgstr "Sie können nicht mehr als %s Produkte pro Bestellung auswählen" @@ -860,6 +870,151 @@ msgstr "" msgid "This voucher is expired" msgstr "Dieser Gutschein ist abgelaufen" +#: pretix/base/services/invoices.py:61 +#, python-brace-format +msgctxt "invoice" +msgid "" +"{i.company}\n" +"{i.name}\n" +"{i.street}\n" +"{i.zipcode} {i.city}\n" +"{i.country}" +msgstr "" +"{i.company}\n" +"{i.name}\n" +"{i.street}\n" +"{i.zipcode} {i.city}\n" +"{i.country}" + +#: pretix/base/services/invoices.py:68 +#, python-format +msgctxt "invoice" +msgid "VAT-ID: %s" +msgstr "USt-ID: %s" + +#: pretix/base/services/invoices.py:95 +#, python-brace-format +msgid "Payment via {method}" +msgstr "Zahlung mittels {method}" + +#: pretix/base/services/invoices.py:125 pretix/base/services/invoices.py:134 +#: pretix/plugins/reports/exporters.py:97 +#, python-format +msgid "Page %d" +msgstr "Seite %d" + +#: pretix/base/services/invoices.py:130 +#, python-brace-format +msgctxt "invoice" +msgid "Invoice {num}" +msgstr "Rechnung {num}" + +#: pretix/base/services/invoices.py:138 +msgctxt "invoice" +msgid "Invoice from" +msgstr "Rechnungsaussteller" + +#: pretix/base/services/invoices.py:146 +msgctxt "invoice" +msgid "Invoice to" +msgstr "Rechnungsempfänger" + +#: pretix/base/services/invoices.py:155 +msgctxt "invoice" +msgid "Cancellation number" +msgstr "Korrekturnummer" + +#: pretix/base/services/invoices.py:161 +msgctxt "invoice" +msgid "Original invoice" +msgstr "Ursprüngl. Rechnung" + +#: pretix/base/services/invoices.py:166 +msgctxt "invoice" +msgid "Invoice number" +msgstr "Rechnungsnummer" + +#: pretix/base/services/invoices.py:174 +msgctxt "invoice" +msgid "Cancellation date" +msgstr "Korrekturdatum" + +#: pretix/base/services/invoices.py:180 +msgctxt "invoice" +msgid "Original invoice date" +msgstr "Ursprüngl. Rechnungsdatum" + +#: pretix/base/services/invoices.py:187 +msgctxt "invoice" +msgid "Invoice date" +msgstr "Rechnungsdatum" + +#: pretix/base/services/invoices.py:203 +#: pretix/control/templates/pretixcontrol/attendees/index.html:35 +#: pretix/control/templates/pretixcontrol/order/index.html:60 +#: pretix/control/templates/pretixcontrol/orders/index.html:45 +msgid "Order date" +msgstr "Bestelldatum" + +#: pretix/base/services/invoices.py:217 +#, python-format +msgid "" +"%s\n" +"until %s" +msgstr "" +"%s\n" +"bis %s" + +#: pretix/base/services/invoices.py:244 +msgctxt "invoice" +msgid "Invoice" +msgstr "Rechnung" + +#: pretix/base/services/invoices.py:246 +msgctxt "invoice" +msgid "Cancellation" +msgstr "Rechnungskorrektur" + +#: pretix/base/services/invoices.py:262 +msgctxt "invoice" +msgid "Description" +msgstr "Position" + +#: pretix/base/services/invoices.py:262 pretix/base/services/invoices.py:292 +msgctxt "invoice" +msgid "Tax rate" +msgstr "Steuersatz" + +#: pretix/base/services/invoices.py:262 +msgctxt "invoice" +msgid "Price" +msgstr "Betrag" + +#: pretix/base/services/invoices.py:274 +msgctxt "invoice" +msgid "Invoice total" +msgstr "Rechnungsbetrag" + +#: pretix/base/services/invoices.py:291 +msgctxt "invoice" +msgid "Included taxes" +msgstr "Enthaltene Umsatzsteuer" + +#: pretix/base/services/invoices.py:293 +msgctxt "invoice" +msgid "Net value" +msgstr "Netto" + +#: pretix/base/services/invoices.py:293 +msgctxt "invoice" +msgid "Gross value" +msgstr "Brutto" + +#: pretix/base/services/invoices.py:293 +msgctxt "invoice" +msgid "Tax" +msgstr "Steuer" + #: pretix/base/services/mail.py:64 #, python-format msgid "You are receiving this e-mail because you placed an order for %s." @@ -867,7 +1022,7 @@ msgstr "" "Sie erhalten diese E-Mail, weil Sie eine Bestellung für die Veranstaltung %s " "getätigt haben." -#: pretix/base/services/orders.py:26 +#: pretix/base/services/orders.py:29 msgid "" "The price of some of the items in your cart has changed in the meantime. " "Please see below for details." @@ -875,26 +1030,26 @@ msgstr "" "Der Preis einiger Produkte in Ihrem Warenkorb hat sich zwischenzeitlich " "geändert." -#: pretix/base/services/orders.py:29 +#: pretix/base/services/orders.py:32 msgid "An internal error occured, please try again." msgstr "Ein interner Fehler ist aufgetreten, bitte erneut versuchen." -#: pretix/base/services/orders.py:32 +#: pretix/base/services/orders.py:35 msgid "A voucher you tried to use already has been used." msgstr "" "Ein Gutscheincode, den Sie verwenden wollten, wurde zwischenzeitlich bereits " "eingelöst." -#: pretix/base/services/orders.py:33 +#: pretix/base/services/orders.py:36 msgid "A voucher you tried to use has expired." msgstr "Ein Gutscheincode, den Sie verwenden wollten, ist abgelaufen." -#: pretix/base/services/orders.py:77 +#: pretix/base/services/orders.py:80 #, python-format msgid "Payment received for your order: %(code)s" msgstr "Zahlung erhalten für die Bestellung: %(code)s" -#: pretix/base/services/orders.py:254 pretix/control/views/orders.py:208 +#: pretix/base/services/orders.py:270 pretix/control/views/orders.py:210 #: pretix/presale/templates/pretixpresale/event/order.html:21 #, python-format msgid "Your order: %(code)s" @@ -908,7 +1063,7 @@ msgstr "Unkategorisiert" msgid "Payment method fees" msgstr "Zahlungsgebühren" -#: pretix/base/settings.py:114 +#: pretix/base/settings.py:134 #, python-brace-format msgid "" "Hello,\n" @@ -934,7 +1089,7 @@ msgstr "" "Viele Grüße,\n" "Das {event} Team" -#: pretix/base/settings.py:127 +#: pretix/base/settings.py:147 #, python-brace-format msgid "" "Hello,\n" @@ -963,7 +1118,7 @@ msgstr "" "Viele Grüße,\n" "Das {event}s Team" -#: pretix/base/settings.py:142 +#: pretix/base/settings.py:162 #, python-brace-format msgid "" "Hello,\n" @@ -1213,103 +1368,146 @@ msgstr "" "Das späteste Datum, zu dem Benutzer die Details ihrer Bestellung (z.B. Namen " "oder Antworten zu Fragen) verändern können." +#: pretix/control/forms/event.py:112 +msgid "Tax rate for payment fees" +msgstr "Steuersatz für Zahlungsgebühren" + #: pretix/control/forms/event.py:113 +msgid "" +"The tax rate that applies for additional fees you configured for single " +"payment methods (in percent)." +msgstr "" +"Der Steuersatz der auf Gebühren für Zahlungsmethoden angewendet werden soll." + +#: pretix/control/forms/event.py:118 msgid "Default timezone" msgstr "Standardzeitzone" -#: pretix/control/forms/event.py:117 +#: pretix/control/forms/event.py:122 msgid "Available langauges" msgstr "Verfügbare Sprachen" -#: pretix/control/forms/event.py:121 +#: pretix/control/forms/event.py:126 msgid "Default language" msgstr "Standardsprache" -#: pretix/control/forms/event.py:124 +#: pretix/control/forms/event.py:129 msgid "Ask for attendee names" msgstr "Namen der Teilnehmer erfragen" -#: pretix/control/forms/event.py:125 +#: pretix/control/forms/event.py:130 msgid "Ask for a name for all tickets which include admission to the event." msgstr "Frage für alle Eintritts-Tickets nach dem Namen des Teilnehmers." -#: pretix/control/forms/event.py:129 +#: pretix/control/forms/event.py:134 msgid "Require attendee names" msgstr "Namen des Teilnehmer erfordern" -#: pretix/control/forms/event.py:130 +#: pretix/control/forms/event.py:135 msgid "Require customers to fill in the names of all attendees." msgstr "Erfordere die Eingabe aller Teilnehmer-Namen." -#: pretix/control/forms/event.py:134 +#: pretix/control/forms/event.py:139 msgid "Ask for invoice address" msgstr "Rechnungsadresse erfragen" -#: pretix/control/forms/event.py:138 +#: pretix/control/forms/event.py:143 msgid "Require invoice address" msgstr "Rechnungsadresse erforderlich" -#: pretix/control/forms/event.py:142 +#: pretix/control/forms/event.py:147 msgid "Ask for VAT ID" msgstr "Nach USt-ID fragen" -#: pretix/control/forms/event.py:143 +#: pretix/control/forms/event.py:148 msgid "" "Does only work if an invoice address is asked for. VAT ID is not required." msgstr "" "Funktioniert nur, wenn nach einer Rechnungsadresse gefragt wird. Die USt-ID-" "Eingabe ist freiwillig." -#: pretix/control/forms/event.py:148 +#: pretix/control/forms/event.py:152 +msgid "Generate invoices" +msgstr "Rechnungen ausstellen" + +#: pretix/control/forms/event.py:157 +msgid "Your address" +msgstr "Ihre Adresse" + +#: pretix/control/forms/event.py:158 +msgid "" +"Will be printed as the sender on invoices. Be sure to include relevant " +"details required in your jurisdiction (e.g. your VAT ID)." +msgstr "" +"Wird als Absender auf Rechnungen gedruckt. Denken Sie daran, alle rechtlich " +"erforderlichen Merkmale aufzuführen, z.B. Ihre USt-ID oder Steuernummer." + +#: pretix/control/forms/event.py:163 +msgid "Additional text" +msgstr "Zusätzlicher Text" + +#: pretix/control/forms/event.py:164 +msgid "Will be printed on every invoice below the invoice total." +msgstr "Wird auf jeder Rechnung unterhalb des Gesamtbetrages gedruckt." + +#: pretix/control/forms/event.py:168 +msgid "Invoice language" +msgstr "Rechnungssprache" + +#: pretix/control/forms/event.py:169 +msgid "The user's language" +msgstr "Sprache des Benutzers" + +#: pretix/control/forms/event.py:173 msgid "Maximum number of items per order" msgstr "Maximale Anzahl Produkte pro Bestellung" -#: pretix/control/forms/event.py:152 +#: pretix/control/forms/event.py:177 msgid "Reservation period" msgstr "Reservierungszeitraum" -#: pretix/control/forms/event.py:153 +#: pretix/control/forms/event.py:178 msgid "" "The number of minutes the items in a user's card are reserved for this user." msgstr "" "Die Dauer in Minuten, die Produkte im Warenkorb eines Benutzers reserviert " "werden." -#: pretix/control/forms/event.py:156 +#: pretix/control/forms/event.py:181 msgid "Imprint URL" msgstr "Impressum (URL)" -#: pretix/control/forms/event.py:160 +#: pretix/control/forms/event.py:185 msgid "Contact address" msgstr "Kontakt-E-Mail" -#: pretix/control/forms/event.py:162 +#: pretix/control/forms/event.py:187 msgid "Public email address for contacting the organizer" msgstr "Öffentliche E-Mail-Adresse zur Kontaktierung des Veranstalters" -#: pretix/control/forms/event.py:169 +#: pretix/control/forms/event.py:194 msgid "" "Your default locale must also be enebled for your event (see box above)." msgstr "" "Die Standardsprache muss eine der aktivierten Sprachen sein (siehe weiter " "oben)." -#: pretix/control/forms/event.py:173 +#: pretix/control/forms/event.py:198 msgid "" "You cannot require specifying attendee names if you do not ask for them." msgstr "" "Sie können die Angabe von Teilnehmernamen nur erfordern, wenn Sie auch nach " "Namen fragen." -#: pretix/control/forms/event.py:202 pretix/control/forms/event.py:304 +#: pretix/control/forms/event.py:227 pretix/control/forms/event.py:329 msgid "This field is required." msgstr "Dieses Feld ist erforderlich." -#: pretix/control/forms/event.py:207 +#: pretix/control/forms/event.py:232 msgid "Subject prefix" msgstr "Betreffs-Prefix" -#: pretix/control/forms/event.py:208 +#: pretix/control/forms/event.py:233 msgid "" "This will be prepended to the subject of all outgoing emails. This could be " "a short form of your event name." @@ -1317,19 +1515,19 @@ msgstr "" "Dies wird jedem E-Mail-Betreff vorangestellt und könnten z.B. eine Kurzform " "Ihres Veranstaltungsnamens enthalten." -#: pretix/control/forms/event.py:213 +#: pretix/control/forms/event.py:238 msgid "Sender address" msgstr "Absender-Adresse" -#: pretix/control/forms/event.py:214 +#: pretix/control/forms/event.py:239 msgid "Sender address for outgoing e-mails" msgstr "Absender-Adresse für ausgehende E-Mails" -#: pretix/control/forms/event.py:217 +#: pretix/control/forms/event.py:242 msgid "Placed order" msgstr "Getätigte Bestellung" -#: pretix/control/forms/event.py:220 +#: pretix/control/forms/event.py:245 #, python-brace-format msgid "" "Available placeholders: {event}, {total}, {currency}, {date}, {paymentinfo}, " @@ -1338,25 +1536,25 @@ msgstr "" "Verfügbare Platzhalter: {event}, {total}, {currency}, {date}, {paymentinfo}, " "{url}" -#: pretix/control/forms/event.py:223 +#: pretix/control/forms/event.py:248 msgid "Paid order" msgstr "Bezahlt Bestellung" -#: pretix/control/forms/event.py:226 pretix/control/forms/event.py:232 +#: pretix/control/forms/event.py:251 pretix/control/forms/event.py:257 #, python-brace-format msgid "Available placeholders: {event}, {url}" msgstr "Verfügbare Platzhalter: {event}, {url}" -#: pretix/control/forms/event.py:229 +#: pretix/control/forms/event.py:254 #: pretix/control/templates/pretixcontrol/order/index.html:76 msgid "Resend link" msgstr "Link erneut senden" -#: pretix/control/forms/event.py:235 +#: pretix/control/forms/event.py:260 msgid "Use custom SMTP server" msgstr "Eigenen SMTP-Server verwenden" -#: pretix/control/forms/event.py:236 +#: pretix/control/forms/event.py:261 msgid "" "All mail related to your event will be sent over the smtp server specified " "by you." @@ -1364,49 +1562,49 @@ msgstr "" "Alle E-Mails bezüglich Ihrer Veranstaltung werden über den von Ihnen " "angegebenen SMTP-Server versendet." -#: pretix/control/forms/event.py:240 +#: pretix/control/forms/event.py:265 msgid "Hostname" msgstr "Hostname" -#: pretix/control/forms/event.py:244 +#: pretix/control/forms/event.py:269 msgid "Port" msgstr "Port" -#: pretix/control/forms/event.py:248 +#: pretix/control/forms/event.py:273 msgid "Username" msgstr "Benutzername" -#: pretix/control/forms/event.py:257 +#: pretix/control/forms/event.py:282 msgid "Use STARTTLS" msgstr "STARTTLS verwenden" -#: pretix/control/forms/event.py:258 +#: pretix/control/forms/event.py:283 msgid "Commonly enabled on port 587." msgstr "Meistens auf Port 587 verfügbar." -#: pretix/control/forms/event.py:262 +#: pretix/control/forms/event.py:287 msgid "Use SSL" msgstr "SSL verwenden" -#: pretix/control/forms/event.py:263 +#: pretix/control/forms/event.py:288 msgid "Commonly enabled on port 465." msgstr "Meistens auf Port 465 verfügbar." -#: pretix/control/forms/event.py:278 +#: pretix/control/forms/event.py:303 msgid "Use feature" msgstr "Funktion benutzen" -#: pretix/control/forms/event.py:279 +#: pretix/control/forms/event.py:304 msgid "Use pretix to generate tickets for the user to download and print out." msgstr "" "Wenn diese Funktion aktiviert ist, generiert pretix Tickets, die der " "Benutzer herunterladen und ausdrucken kann." -#: pretix/control/forms/event.py:283 +#: pretix/control/forms/event.py:308 msgid "Download date" msgstr "Download-Datum" -#: pretix/control/forms/event.py:284 +#: pretix/control/forms/event.py:309 msgid "Ticket download will be offered after this date." msgstr "Der Ticket-Download wird zu diesem Zeitpunkt freigeschaltet." @@ -1514,12 +1712,6 @@ msgstr "Alle Tickets" msgid "Filter" msgstr "Filter" -#: pretix/control/templates/pretixcontrol/attendees/index.html:35 -#: pretix/control/templates/pretixcontrol/order/index.html:60 -#: pretix/control/templates/pretixcontrol/orders/index.html:45 -msgid "Order date" -msgstr "Bestelldatum" - #: pretix/control/templates/pretixcontrol/auth/base.html:29 #: pretix/control/templates/pretixcontrol/base.html:126 #: pretix/presale/templates/pretixpresale/base.html:33 @@ -1557,7 +1749,7 @@ msgstr "Neues Passwort setzen" #: pretix/control/templates/pretixcontrol/event/mail.html:30 #: pretix/control/templates/pretixcontrol/event/payment.html:31 #: pretix/control/templates/pretixcontrol/event/permissions.html:56 -#: pretix/control/templates/pretixcontrol/event/settings.html:50 +#: pretix/control/templates/pretixcontrol/event/settings.html:58 #: pretix/control/templates/pretixcontrol/event/tickets.html:36 #: pretix/control/templates/pretixcontrol/events/create.html:30 #: pretix/control/templates/pretixcontrol/item/index.html:31 @@ -1637,7 +1829,7 @@ msgstr "Allgemeines" #: pretix/control/templates/pretixcontrol/event/base.html:31 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:51 #: pretix/presale/templates/pretixpresale/event/order.html:31 -#: pretix/presale/templates/pretixpresale/event/order.html:148 +#: pretix/presale/templates/pretixpresale/event/order.html:175 msgid "Payment" msgstr "Zahlung" @@ -1833,9 +2025,9 @@ msgstr "Anzeigeeinstellungen" msgid "Timeline" msgstr "Ablauf" -#: pretix/control/templates/pretixcontrol/event/settings.html:39 -msgid "Order process settings" -msgstr "Bestellzeile" +#: pretix/control/templates/pretixcontrol/event/settings.html:46 +msgid "Invoicing" +msgstr "Rechnungswesen" #: pretix/control/templates/pretixcontrol/event/tickets.html:8 #: pretix/presale/templates/pretixpresale/event/order.html:52 @@ -2117,7 +2309,7 @@ msgstr "Verbleibende Kapazität" #: pretix/control/templates/pretixcontrol/order/cancel.html:4 #: pretix/control/templates/pretixcontrol/order/cancel.html:8 #: pretix/control/templates/pretixcontrol/order/index.html:28 -#: pretix/presale/templates/pretixpresale/event/order.html:138 +#: pretix/presale/templates/pretixpresale/event/order.html:165 #: pretix/presale/templates/pretixpresale/event/order_cancel.html:4 msgid "Cancel order" msgstr "Stornieren" @@ -2175,70 +2367,87 @@ msgstr "Bestellung" msgid "Expiry date" msgstr "Ablaufdatum" -#: pretix/control/templates/pretixcontrol/order/index.html:86 +#: pretix/control/templates/pretixcontrol/order/index.html:81 +#: pretix/presale/templates/pretixpresale/event/order.html:100 +msgid "Invoices" +msgstr "Rechnungen" + +#: pretix/control/templates/pretixcontrol/order/index.html:85 +#: pretix/presale/templates/pretixpresale/event/order.html:108 +msgid "Cancellation" +msgstr "Rechnungskorrektur" + +#: pretix/control/templates/pretixcontrol/order/index.html:85 +#: pretix/presale/templates/pretixpresale/event/order.html:108 +msgid "Invoice" +msgstr "Rechnung" + +#: pretix/control/templates/pretixcontrol/order/index.html:99 #: pretix/presale/templates/pretixpresale/event/order.html:87 msgid "Ordered items" msgstr "Bestellte Produkte" -#: pretix/control/templates/pretixcontrol/order/index.html:98 +#: pretix/control/templates/pretixcontrol/order/index.html:111 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:11 msgid "Voucher code used:" msgstr "Verwendeter Gutscheincode:" -#: pretix/control/templates/pretixcontrol/order/index.html:108 -#: pretix/control/templates/pretixcontrol/order/index.html:113 +#: pretix/control/templates/pretixcontrol/order/index.html:121 +#: pretix/control/templates/pretixcontrol/order/index.html:126 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:17 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:21 msgid "not answered" msgstr "nicht beantwortet" -#: pretix/control/templates/pretixcontrol/order/index.html:128 +#: pretix/control/templates/pretixcontrol/order/index.html:141 +#: pretix/control/templates/pretixcontrol/order/index.html:158 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:63 +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:81 #: pretix/presale/templates/pretixpresale/event/index.html:104 #: pretix/presale/templates/pretixpresale/event/index.html:141 #, python-format msgid "incl. %(rate)s%% taxes" msgstr "inkl. %(rate)s%% MwSt." -#: pretix/control/templates/pretixcontrol/order/index.html:150 +#: pretix/control/templates/pretixcontrol/order/index.html:168 #: pretix/control/templates/pretixcontrol/orders/overview.html:85 #: pretix/plugins/reports/exporters.py:199 -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:85 +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:91 msgid "Total" msgstr "Gesamt" -#: pretix/control/templates/pretixcontrol/order/index.html:168 +#: pretix/control/templates/pretixcontrol/order/index.html:186 msgid "The payment state of this order was manually modified." msgstr "Der Status dieser Bestellung wurde manuell verändert." -#: pretix/control/templates/pretixcontrol/order/index.html:173 +#: pretix/control/templates/pretixcontrol/order/index.html:191 #, python-format msgid "The payment has to be completed before %(date)s." msgstr "Die Zahlung muss bis zum %(date)s abgeschlossen sein." -#: pretix/control/templates/pretixcontrol/order/index.html:183 +#: pretix/control/templates/pretixcontrol/order/index.html:201 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:70 #: pretix/presale/templates/pretixpresale/event/checkout_questions.html:30 -#: pretix/presale/templates/pretixpresale/event/order.html:106 +#: pretix/presale/templates/pretixpresale/event/order.html:130 #: pretix/presale/templates/pretixpresale/event/order_modify.html:19 msgid "Invoice information" msgstr "Rechnungsinformationen" -#: pretix/control/templates/pretixcontrol/order/index.html:188 +#: pretix/control/templates/pretixcontrol/order/index.html:206 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:75 -#: pretix/presale/templates/pretixpresale/event/order.html:111 +#: pretix/presale/templates/pretixpresale/event/order.html:135 msgid "Company" msgstr "Firma" -#: pretix/control/templates/pretixcontrol/order/index.html:194 +#: pretix/control/templates/pretixcontrol/order/index.html:212 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:81 -#: pretix/presale/templates/pretixpresale/event/order.html:117 +#: pretix/presale/templates/pretixpresale/event/order.html:141 msgid "ZIP code and city" msgstr "PLZ und Ort" -#: pretix/control/templates/pretixcontrol/order/index.html:198 +#: pretix/control/templates/pretixcontrol/order/index.html:216 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:85 -#: pretix/presale/templates/pretixpresale/event/order.html:121 +#: pretix/presale/templates/pretixpresale/event/order.html:145 msgid "Phone" msgstr "Telefon" @@ -2573,39 +2782,51 @@ msgstr "Das ausgewählte Produkt wurde deaktiviert." msgid "The new event has been created." msgstr "Eine neue Veranstaltung wurde erstellt." -#: pretix/control/views/orders.py:172 +#: pretix/control/views/orders.py:174 msgid "The order has been marked as paid." msgstr "Die Bestellung wurde als bezahlt markiert." -#: pretix/control/views/orders.py:175 +#: pretix/control/views/orders.py:177 msgid "The order has been cancelled." msgstr "Die Bestellung wurde storniert." -#: pretix/control/views/orders.py:181 +#: pretix/control/views/orders.py:183 msgid "The order has been marked as not paid." msgstr "Die Bestellung wurde als unbezahlt markiert." -#: pretix/control/views/orders.py:219 +#: pretix/control/views/orders.py:221 msgid "The email has been queued to be sent." msgstr "Die E-Mail wurde zum Versenden gespeichert." -#: pretix/control/views/orders.py:236 pretix/presale/views/order.py:294 +#: pretix/control/views/orders.py:243 pretix/presale/views/order.py:341 +msgid "This invoice has not been found" +msgstr "Diese Rechnung wurde nicht gefunden" + +#: pretix/control/views/orders.py:251 pretix/presale/views/order.py:349 +msgid "" +"The invoice file has not yet been generated, we will generate it for you " +"now. Please try again in a few seconds." +msgstr "" +"Diese Rechnung wurde bisher noch nicht fertig erstellt, wir werden die PDF-" +"Datei jetzt erstellen. Bitte probieren Sie es in wenigen Sekunden erneut." + +#: pretix/control/views/orders.py:270 pretix/presale/views/order.py:298 msgid "You requested an invalid ticket output type." msgstr "Sie haben einen ungültigen Ausgabetyp gewählt." -#: pretix/control/views/orders.py:239 pretix/presale/views/order.py:299 +#: pretix/control/views/orders.py:273 pretix/presale/views/order.py:303 msgid "Order is not paid." msgstr "Bestellung ist nicht bezahlt." -#: pretix/control/views/orders.py:265 pretix/control/views/orders.py:303 +#: pretix/control/views/orders.py:299 pretix/control/views/orders.py:337 msgid "This action is only allowed for pending orders." msgstr "Diese Aktion ist nur für unbezahlte Bestellungen möglich." -#: pretix/control/views/orders.py:271 pretix/control/views/orders.py:285 +#: pretix/control/views/orders.py:305 pretix/control/views/orders.py:319 msgid "The payment term has been changed." msgstr "Die Zahlungsfrist wurde geändert." -#: pretix/control/views/orders.py:289 +#: pretix/control/views/orders.py:323 msgid "" "We were not able to process the request completely as the server was too " "busy." @@ -2613,15 +2834,15 @@ msgstr "" "Wir konnten Ihre Bestellung nicht durchführen, da der Server zu beschäftigt " "war." -#: pretix/control/views/orders.py:338 +#: pretix/control/views/orders.py:372 msgid "There is no order with the given order code." msgstr "Es existiert keine Bestellung mit der eingegebenen Bestellnummer." -#: pretix/control/views/orders.py:372 +#: pretix/control/views/orders.py:406 msgid "The selected exporter was not found." msgstr "Das ausgewählte Exportformat wurde nicht gefunden." -#: pretix/control/views/orders.py:378 +#: pretix/control/views/orders.py:412 msgid "There was a problem processing your input. See below for error details." msgstr "Die Eingabe konnte nicht verarbeitet werden." @@ -3148,11 +3369,6 @@ msgid "This plugin allows you to generate printable reports about your sales." msgstr "" "Dieses Plugin erlaubt, ausdruckbare Berichte über Ihre Verkäufe zu erstellen." -#: pretix/plugins/reports/exporters.py:97 -#, python-format -msgid "Page %d" -msgstr "Seite %d" - #: pretix/plugins/reports/exporters.py:99 #, python-format msgid "Created: %s" @@ -3489,7 +3705,7 @@ msgstr "Veranstaltungsname-y-Position (mm)" msgid "Event name size (mm)" msgstr "Veranstaltungsname-Größe (mm)" -#: pretix/presale/checkoutflow.py:166 pretix/presale/views/order.py:230 +#: pretix/presale/checkoutflow.py:166 pretix/presale/views/order.py:234 msgid "" "We had difficulties processing your input. Please review the errors below." msgstr "Wir hatten Schwierigkeiten, Ihre Eingabe zu verarbeiten." @@ -3507,15 +3723,15 @@ msgid "Please select a payment method." msgstr "Bitte wählen Sie eine Zahlungsmethode aus." #: pretix/presale/checkoutflow.py:274 pretix/presale/checkoutflow.py:280 -#: pretix/presale/views/order.py:155 pretix/presale/views/order.py:184 +#: pretix/presale/views/order.py:159 pretix/presale/views/order.py:188 msgid "The payment information you entered was incomplete." msgstr "Die eingegebenen Zahlungsinformationen sind unvollständig." -#: pretix/presale/forms/checkout.py:19 +#: pretix/presale/forms/checkout.py:21 msgid "Street and Number" msgstr "Straße und Hausnummer" -#: pretix/presale/forms/checkout.py:37 +#: pretix/presale/forms/checkout.py:39 msgid "You need to provide either a company name or your name." msgstr "Sie müssen entweder einen Firmennamen oder Ihren Namen eingeben." @@ -3725,7 +3941,7 @@ msgid "You will be able to download your tickets here on %(date)s." msgstr "Sie können Ihre Tickets hier ab %(date)s herunterladen." #: pretix/presale/templates/pretixpresale/event/order.html:82 -#: pretix/presale/templates/pretixpresale/event/order.html:101 +#: pretix/presale/templates/pretixpresale/event/order.html:125 msgid "Change details" msgstr "Details bearbeiten" @@ -3831,27 +4047,27 @@ msgstr "Die gewählten Produkte wurden Ihrem Warenkorb hinzugefügt." msgid "Your cart is empty" msgstr "Ihr Warenkorb ist leer." -#: pretix/presale/views/order.py:55 pretix/presale/views/order.py:107 -#: pretix/presale/views/order.py:149 pretix/presale/views/order.py:181 -#: pretix/presale/views/order.py:243 pretix/presale/views/order.py:264 -#: pretix/presale/views/order.py:297 +#: pretix/presale/views/order.py:58 pretix/presale/views/order.py:111 +#: pretix/presale/views/order.py:153 pretix/presale/views/order.py:185 +#: pretix/presale/views/order.py:247 pretix/presale/views/order.py:268 +#: pretix/presale/views/order.py:301 pretix/presale/views/order.py:332 msgid "Unknown order code or not authorized to access this order." msgstr "" "Unbekannte Bestellnummer oder Bestellung gehört einem anderen Benutzer." -#: pretix/presale/views/order.py:111 pretix/presale/views/order.py:151 +#: pretix/presale/views/order.py:115 pretix/presale/views/order.py:155 msgid "The payment for this order cannot be continued." msgstr "Diese Bestellung kann nicht mehr bezahlt werden." -#: pretix/presale/views/order.py:245 +#: pretix/presale/views/order.py:249 msgid "You cannot modify this order" msgstr "Sie können diese Bestellung nicht bearbeiten" -#: pretix/presale/views/order.py:266 +#: pretix/presale/views/order.py:270 msgid "You cannot cancel this order" msgstr "Sie können diese Bestellung nicht stornieren" -#: pretix/presale/views/order.py:304 +#: pretix/presale/views/order.py:308 msgid "Ticket download is not (yet) enabled." msgstr "Der Ticket-Download ist (noch) nicht freigeschaltet." @@ -3867,6 +4083,9 @@ msgstr "Deutsch" msgid "German (informal)" msgstr "Deutsch (Du)" +#~ msgid "Order process settings" +#~ msgstr "Bestellzeile" + #~ msgid "Tickets sold" #~ msgstr "Tickets verkauft" @@ -3963,9 +4182,6 @@ msgstr "Deutsch (Du)" #~ msgid "Value" #~ msgstr "Wert" -#~ msgid "Property value" -#~ msgstr "Wert" - #~ msgid "Property values" #~ msgstr "Werte" @@ -4134,9 +4350,6 @@ msgstr "Deutsch (Du)" #~ msgid "Welcome back!" #~ msgstr "Willkommen zurück!" -#~ msgid "Your orders" -#~ msgstr "Ihre Bestellungen" - #~ msgid "Place new order" #~ msgstr "Neue Bestellung" diff --git a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po index 1b776fd9f9..72fd04de57 100644 --- a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po +++ b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-10 22:11+0000\n" -"PO-Revision-Date: 2016-03-10 23:11+0100\n" +"POT-Creation-Date: 2016-03-13 14:52+0000\n" +"PO-Revision-Date: 2016-03-13 15:58+0100\n" "Last-Translator: Raphael Michel \n" "Language-Team: Raphael Michel \n" "Language: de\n" @@ -28,12 +28,12 @@ msgstr "aktiviert" #: pretix/base/forms/auth.py:13 pretix/base/forms/auth.py:140 #: pretix/base/models/auth.py:63 pretix/base/models/orders.py:100 -#: pretix/presale/forms/checkout.py:10 +#: pretix/presale/forms/checkout.py:12 msgid "E-mail" msgstr "E-Mail" #: pretix/base/forms/auth.py:14 pretix/base/forms/auth.py:77 -#: pretix/base/forms/auth.py:113 pretix/control/forms/event.py:252 +#: pretix/base/forms/auth.py:113 pretix/control/forms/event.py:277 msgid "Password" msgstr "Passwort" @@ -149,10 +149,10 @@ msgid "%(family)s, %(given)s" msgstr "%(family)s, %(given)s" #: pretix/base/models/event.py:55 pretix/base/models/items.py:421 -#: pretix/base/models/orders.py:477 pretix/base/models/organizer.py:25 -#: pretix/control/templates/pretixcontrol/order/index.html:190 +#: pretix/base/models/orders.py:494 pretix/base/models/organizer.py:25 +#: pretix/control/templates/pretixcontrol/order/index.html:208 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:77 -#: pretix/presale/templates/pretixpresale/event/order.html:113 +#: pretix/presale/templates/pretixpresale/event/order.html:137 #: pretix/presale/templates/pretixpresale/organizers/index.html:21 msgid "Name" msgstr "Name" @@ -228,7 +228,8 @@ msgstr "Erweiterungen" #: pretix/base/models/event.py:99 pretix/base/models/items.py:109 #: pretix/base/models/items.py:417 pretix/base/models/orders.py:95 -#: pretix/base/models/orders.py:439 pretix/base/models/vouchers.py:25 +#: pretix/base/models/orders.py:456 pretix/base/models/vouchers.py:25 +#: pretix/base/services/invoices.py:211 msgid "Event" msgstr "Veranstaltung" @@ -446,7 +447,7 @@ msgstr "Gesamtanzahl" msgid "Leave empty for an unlimited number of tickets." msgstr "Leer lassen für unbegrenzt viele Tickets." -#: pretix/base/models/items.py:430 pretix/base/models/orders.py:299 +#: pretix/base/models/items.py:430 pretix/base/models/orders.py:317 msgid "Item" msgstr "Produkt" @@ -488,7 +489,7 @@ msgstr "storniert" msgid "refunded" msgstr "erstattet" -#: pretix/base/models/orders.py:86 +#: pretix/base/models/orders.py:86 pretix/base/services/invoices.py:197 #: pretix/control/templates/pretixcontrol/attendees/index.html:32 #: pretix/control/templates/pretixcontrol/order/index.html:58 #: pretix/control/templates/pretixcontrol/orders/index.html:10 @@ -508,14 +509,14 @@ msgstr "Status" msgid "Locale" msgstr "Sprache" -#: pretix/base/models/orders.py:108 pretix/base/models/orders.py:446 +#: pretix/base/models/orders.py:108 pretix/base/models/orders.py:463 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_assign.html:17 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_confirm.html:13 #: pretix/presale/templates/pretixpresale/organizers/index.html:22 msgid "Date" msgstr "Datum" -#: pretix/base/models/orders.py:111 pretix/base/models/orders.py:450 +#: pretix/base/models/orders.py:111 pretix/base/models/orders.py:467 #: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form.html:23 msgid "Expiration date" msgstr "Ablaufdatum" @@ -531,126 +532,135 @@ msgid "Payment provider" msgstr "Zahlungsmethode" #: pretix/base/models/orders.py:124 -#: pretix/control/templates/pretixcontrol/order/index.html:140 +#: pretix/control/templates/pretixcontrol/order/index.html:152 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:75 msgid "Payment method fee" msgstr "Gebühr für gewählte Zahlungsmethode" -#: pretix/base/models/orders.py:127 -#: pretix/control/templates/pretixcontrol/order/index.html:162 +#: pretix/base/models/orders.py:128 +msgid "Payment method fee tax rate" +msgstr "Steuersatz auf Gebühr für gewählte Zahlungsmethode" + +#: pretix/base/models/orders.py:132 +msgid "Payment method fee tax" +msgstr "Steuerbetrag auf Gebühr für gewählte Zahlungsmethode" + +#: pretix/base/models/orders.py:135 +#: pretix/control/templates/pretixcontrol/order/index.html:180 msgid "Payment information" msgstr "Zahlungsinformationen" -#: pretix/base/models/orders.py:131 +#: pretix/base/models/orders.py:139 msgid "Payment state was manually modified" msgstr "Der Bestellungsstatus wurde manuell verändert" -#: pretix/base/models/orders.py:136 +#: pretix/base/models/orders.py:144 msgid "Total amount" msgstr "Gesamtbetrag" -#: pretix/base/models/orders.py:140 pretix/base/models/orders.py:367 +#: pretix/base/models/orders.py:148 pretix/base/models/orders.py:385 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_confirm.html:17 msgid "Order" msgstr "Bestellung" -#: pretix/base/models/orders.py:141 +#: pretix/base/models/orders.py:149 #: pretix/control/templates/pretixcontrol/event/base.html:102 +#: pretix/control/templates/pretixcontrol/event/settings.html:39 #: pretix/control/templates/pretixcontrol/orders/index.html:3 #: pretix/control/templates/pretixcontrol/orders/index.html:5 msgid "Orders" msgstr "Bestellungen" -#: pretix/base/models/orders.py:190 +#: pretix/base/models/orders.py:208 msgid "The payment is too late to be accepted." msgstr "Die Zahlung kommt zu spät, um akzeptiert werden zu können." -#: pretix/base/models/orders.py:205 +#: pretix/base/models/orders.py:223 msgid "Some of the ordered products were no longer available." msgstr "Einige der ausgewählten Produkte sind nicht mehr verfügbar." -#: pretix/base/models/orders.py:305 +#: pretix/base/models/orders.py:323 msgid "Variation" msgstr "Variante" -#: pretix/base/models/orders.py:310 +#: pretix/base/models/orders.py:328 msgid "Price" msgstr "Preis" -#: pretix/base/models/orders.py:314 +#: pretix/base/models/orders.py:332 #: pretix/control/templates/pretixcontrol/attendees/index.html:34 -#: pretix/control/templates/pretixcontrol/order/index.html:106 -#: pretix/presale/forms/checkout.py:65 +#: pretix/control/templates/pretixcontrol/order/index.html:119 +#: pretix/presale/forms/checkout.py:67 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:16 msgid "Attendee name" msgstr "Name des Teilnehmers" -#: pretix/base/models/orders.py:316 +#: pretix/base/models/orders.py:334 msgid "Empty, if this product is not an admission ticket" msgstr "Leer, wenn dies kein Eintrittsticket ist" -#: pretix/base/models/orders.py:373 +#: pretix/base/models/orders.py:391 msgid "Tax rate" msgstr "Steuersatz" -#: pretix/base/models/orders.py:377 +#: pretix/base/models/orders.py:395 msgid "Tax value" msgstr "Steuer" -#: pretix/base/models/orders.py:382 +#: pretix/base/models/orders.py:400 msgid "Order position" msgstr "Bestelltes Produkt" -#: pretix/base/models/orders.py:383 +#: pretix/base/models/orders.py:401 msgid "Order positions" msgstr "Bestellzeile" -#: pretix/base/models/orders.py:443 +#: pretix/base/models/orders.py:460 msgid "Cart ID (e.g. session key)" msgstr "Warenkorb-ID (z.B. Session-ID)" -#: pretix/base/models/orders.py:454 +#: pretix/base/models/orders.py:471 msgid "Cart position" msgstr "Produkt im Warenkorb" -#: pretix/base/models/orders.py:455 +#: pretix/base/models/orders.py:472 msgid "Cart positions" msgstr "Produkte im Warenkorb" -#: pretix/base/models/orders.py:476 +#: pretix/base/models/orders.py:493 msgid "Company name" msgstr "Firmenname" -#: pretix/base/models/orders.py:478 -#: pretix/control/templates/pretixcontrol/order/index.html:192 +#: pretix/base/models/orders.py:495 +#: pretix/control/templates/pretixcontrol/order/index.html:210 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:79 -#: pretix/presale/templates/pretixpresale/event/order.html:115 +#: pretix/presale/templates/pretixpresale/event/order.html:139 msgid "Address" msgstr "Adresse" -#: pretix/base/models/orders.py:479 +#: pretix/base/models/orders.py:496 msgid "ZIP code" msgstr "Postleitzahl" -#: pretix/base/models/orders.py:480 +#: pretix/base/models/orders.py:497 msgid "City" msgstr "Ort" -#: pretix/base/models/orders.py:481 -#: pretix/control/templates/pretixcontrol/order/index.html:196 +#: pretix/base/models/orders.py:498 +#: pretix/control/templates/pretixcontrol/order/index.html:214 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:83 -#: pretix/presale/templates/pretixpresale/event/order.html:119 +#: pretix/presale/templates/pretixpresale/event/order.html:143 msgid "Country" msgstr "Land" -#: pretix/base/models/orders.py:482 +#: pretix/base/models/orders.py:499 msgid "Phone number" msgstr "Telefonnummer" -#: pretix/base/models/orders.py:483 -#: pretix/control/templates/pretixcontrol/order/index.html:201 +#: pretix/base/models/orders.py:500 +#: pretix/control/templates/pretixcontrol/order/index.html:219 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:88 -#: pretix/presale/templates/pretixpresale/event/order.html:124 +#: pretix/presale/templates/pretixpresale/event/order.html:148 msgid "VAT ID" msgstr "USt-ID" @@ -801,7 +811,7 @@ msgstr "Kostenlos" msgid "The order has been marked as refunded." msgstr "Die Bestellung wurde als zurückerstattet markiert." -#: pretix/base/services/cart.py:20 pretix/base/services/orders.py:30 +#: pretix/base/services/cart.py:20 pretix/base/services/orders.py:33 msgid "" "We were not able to process your request completely as the server was too " "busy. Please try again." @@ -817,7 +827,7 @@ msgstr "Du hast keine Produkte ausgewählt." msgid "You selected a product which is not available for sale." msgstr "Du hsat ein Produkt ausgewählt, das nicht zum Verkauf steht." -#: pretix/base/services/cart.py:24 pretix/base/services/orders.py:22 +#: pretix/base/services/cart.py:24 pretix/base/services/orders.py:25 msgid "" "Some of the products you selected were no longer available. Please see below " "for details." @@ -825,7 +835,7 @@ msgstr "" "Einige der ausgewählten Produkte waren zwischenzeitlich nicht mehr " "verfügbar, bitte überprüfe deinen Warenkorb." -#: pretix/base/services/cart.py:26 pretix/base/services/orders.py:24 +#: pretix/base/services/cart.py:26 pretix/base/services/orders.py:27 msgid "" "Some of the products you selected were no longer available in the quantity " "you selected. Please see below for details." @@ -833,7 +843,7 @@ msgstr "" "Einige der ausgewählten Produkte waren zwischenzeitlich nicht mehr in der " "gewünschten Anzahl verfügbar, bitte überprüfe deinen Warenkorb." -#: pretix/base/services/cart.py:28 pretix/base/services/orders.py:28 +#: pretix/base/services/cart.py:28 pretix/base/services/orders.py:31 #, python-format msgid "You cannot select more than %s items per order" msgstr "Du kannst nicht mehr als %s Produkte pro Bestellung auswählen" @@ -860,6 +870,151 @@ msgstr "" msgid "This voucher is expired" msgstr "Dieser Gutschein ist abgelaufen" +#: pretix/base/services/invoices.py:61 +#, python-brace-format +msgctxt "invoice" +msgid "" +"{i.company}\n" +"{i.name}\n" +"{i.street}\n" +"{i.zipcode} {i.city}\n" +"{i.country}" +msgstr "" +"{i.company}\n" +"{i.name}\n" +"{i.street}\n" +"{i.zipcode} {i.city}\n" +"{i.country}" + +#: pretix/base/services/invoices.py:68 +#, python-format +msgctxt "invoice" +msgid "VAT-ID: %s" +msgstr "USt-ID: %s" + +#: pretix/base/services/invoices.py:95 +#, python-brace-format +msgid "Payment via {method}" +msgstr "Zahlung mittels {method}" + +#: pretix/base/services/invoices.py:125 pretix/base/services/invoices.py:134 +#: pretix/plugins/reports/exporters.py:97 +#, python-format +msgid "Page %d" +msgstr "Seite %d" + +#: pretix/base/services/invoices.py:130 +#, python-brace-format +msgctxt "invoice" +msgid "Invoice {num}" +msgstr "Rechnung {num}" + +#: pretix/base/services/invoices.py:138 +msgctxt "invoice" +msgid "Invoice from" +msgstr "Rechnungsaussteller" + +#: pretix/base/services/invoices.py:146 +msgctxt "invoice" +msgid "Invoice to" +msgstr "Rechnungsempfänger" + +#: pretix/base/services/invoices.py:155 +msgctxt "invoice" +msgid "Cancellation number" +msgstr "Korrekturnummer" + +#: pretix/base/services/invoices.py:161 +msgctxt "invoice" +msgid "Original invoice" +msgstr "Ursprüngl. Rechnung" + +#: pretix/base/services/invoices.py:166 +msgctxt "invoice" +msgid "Invoice number" +msgstr "Rechnungsnummer" + +#: pretix/base/services/invoices.py:174 +msgctxt "invoice" +msgid "Cancellation date" +msgstr "Korrekturdatum" + +#: pretix/base/services/invoices.py:180 +msgctxt "invoice" +msgid "Original invoice date" +msgstr "Ursprüngl. Rechnungsdatum" + +#: pretix/base/services/invoices.py:187 +msgctxt "invoice" +msgid "Invoice date" +msgstr "Rechnungsdatum" + +#: pretix/base/services/invoices.py:203 +#: pretix/control/templates/pretixcontrol/attendees/index.html:35 +#: pretix/control/templates/pretixcontrol/order/index.html:60 +#: pretix/control/templates/pretixcontrol/orders/index.html:45 +msgid "Order date" +msgstr "Bestelldatum" + +#: pretix/base/services/invoices.py:217 +#, python-format +msgid "" +"%s\n" +"until %s" +msgstr "" +"%s\n" +"bis %s" + +#: pretix/base/services/invoices.py:244 +msgctxt "invoice" +msgid "Invoice" +msgstr "Rechnung" + +#: pretix/base/services/invoices.py:246 +msgctxt "invoice" +msgid "Cancellation" +msgstr "Rechnungskorrektur" + +#: pretix/base/services/invoices.py:262 +msgctxt "invoice" +msgid "Description" +msgstr "Beschreibung" + +#: pretix/base/services/invoices.py:262 pretix/base/services/invoices.py:292 +msgctxt "invoice" +msgid "Tax rate" +msgstr "Steuersatz" + +#: pretix/base/services/invoices.py:262 +msgctxt "invoice" +msgid "Price" +msgstr "Preis" + +#: pretix/base/services/invoices.py:274 +msgctxt "invoice" +msgid "Invoice total" +msgstr "Rechnungsbetrag" + +#: pretix/base/services/invoices.py:291 +msgctxt "invoice" +msgid "Included taxes" +msgstr "Enthaltene Umsatzsteuer" + +#: pretix/base/services/invoices.py:293 +msgctxt "invoice" +msgid "Net value" +msgstr "Netto" + +#: pretix/base/services/invoices.py:293 +msgctxt "invoice" +msgid "Gross value" +msgstr "Brutto" + +#: pretix/base/services/invoices.py:293 +msgctxt "invoice" +msgid "Tax" +msgstr "Steuer" + #: pretix/base/services/mail.py:64 #, python-format msgid "You are receiving this e-mail because you placed an order for %s." @@ -867,7 +1022,7 @@ msgstr "" "Du erhältst diese E-Mail, weil du eine Bestellung für die Veranstaltung %s " "getätigt hast." -#: pretix/base/services/orders.py:26 +#: pretix/base/services/orders.py:29 msgid "" "The price of some of the items in your cart has changed in the meantime. " "Please see below for details." @@ -875,26 +1030,26 @@ msgstr "" "Der Preis einiger Produkte in deinem Warenkorb hat sich zwischenzeitlich " "geändert." -#: pretix/base/services/orders.py:29 +#: pretix/base/services/orders.py:32 msgid "An internal error occured, please try again." msgstr "Ein interner Fehler ist aufgetreten, bitte erneut versuchen." -#: pretix/base/services/orders.py:32 +#: pretix/base/services/orders.py:35 msgid "A voucher you tried to use already has been used." msgstr "" "Ein Gutscheincode, den du verwenden wolltest, wurde zwischenzeitlich bereits " "eingelöst." -#: pretix/base/services/orders.py:33 +#: pretix/base/services/orders.py:36 msgid "A voucher you tried to use has expired." msgstr "Ein Gutscheincode, den du verwenden wolltest, ist abgelaufen." -#: pretix/base/services/orders.py:77 +#: pretix/base/services/orders.py:80 #, python-format msgid "Payment received for your order: %(code)s" msgstr "Zahlung erhalten für die Bestellung: %(code)s" -#: pretix/base/services/orders.py:254 pretix/control/views/orders.py:208 +#: pretix/base/services/orders.py:270 pretix/control/views/orders.py:210 #: pretix/presale/templates/pretixpresale/event/order.html:21 #, python-format msgid "Your order: %(code)s" @@ -908,7 +1063,7 @@ msgstr "Unkategorisiert" msgid "Payment method fees" msgstr "Zahlungsgebühren" -#: pretix/base/settings.py:114 +#: pretix/base/settings.py:134 #, python-brace-format msgid "" "Hello,\n" @@ -934,7 +1089,7 @@ msgstr "" "Viele Grüße,\n" "Das {event} Team" -#: pretix/base/settings.py:127 +#: pretix/base/settings.py:147 #, python-brace-format msgid "" "Hello,\n" @@ -962,7 +1117,7 @@ msgstr "" "Viele Grüße,\n" "Das {event} Team" -#: pretix/base/settings.py:142 +#: pretix/base/settings.py:162 #, python-brace-format msgid "" "Hello,\n" @@ -1210,103 +1365,146 @@ msgstr "" "Das späteste Datum, zu dem Benutzer die Details ihrer Bestellung (z.B. Namen " "oder Antworten zu Fragen) verändern können." +#: pretix/control/forms/event.py:112 +msgid "Tax rate for payment fees" +msgstr "Steuersatz für Zahlungsgebühren" + #: pretix/control/forms/event.py:113 +msgid "" +"The tax rate that applies for additional fees you configured for single " +"payment methods (in percent)." +msgstr "" +"Der Steuersatz der auf Gebühren für Zahlungsmethoden angewendet werden soll." + +#: pretix/control/forms/event.py:118 msgid "Default timezone" msgstr "Standardzeitzone" -#: pretix/control/forms/event.py:117 +#: pretix/control/forms/event.py:122 msgid "Available langauges" msgstr "Verfügbare Sprachen" -#: pretix/control/forms/event.py:121 +#: pretix/control/forms/event.py:126 msgid "Default language" msgstr "Standardsprache" -#: pretix/control/forms/event.py:124 +#: pretix/control/forms/event.py:129 msgid "Ask for attendee names" msgstr "Namen der Teilnehmer erfragen" -#: pretix/control/forms/event.py:125 +#: pretix/control/forms/event.py:130 msgid "Ask for a name for all tickets which include admission to the event." msgstr "Frage für alle Eintritts-Tickets nach dem Namen des Teilnehmers." -#: pretix/control/forms/event.py:129 +#: pretix/control/forms/event.py:134 msgid "Require attendee names" msgstr "Namen des Teilnehmer erfordern" -#: pretix/control/forms/event.py:130 +#: pretix/control/forms/event.py:135 msgid "Require customers to fill in the names of all attendees." msgstr "Erfordere die Eingabe aller Teilnehmer-Namen." -#: pretix/control/forms/event.py:134 +#: pretix/control/forms/event.py:139 msgid "Ask for invoice address" msgstr "Rechnungsadresse erfragen" -#: pretix/control/forms/event.py:138 +#: pretix/control/forms/event.py:143 msgid "Require invoice address" msgstr "Rechnungsadresse erforderlich" -#: pretix/control/forms/event.py:142 +#: pretix/control/forms/event.py:147 msgid "Ask for VAT ID" msgstr "Nach USt-ID fragen" -#: pretix/control/forms/event.py:143 +#: pretix/control/forms/event.py:148 msgid "" "Does only work if an invoice address is asked for. VAT ID is not required." msgstr "" "Funktioniert nur, wenn nach einer Rechnungsadresse gefragt wird. Die USt-ID-" "Eingabe ist freiwillig." -#: pretix/control/forms/event.py:148 +#: pretix/control/forms/event.py:152 +msgid "Generate invoices" +msgstr "Rechnungen ausstellen" + +#: pretix/control/forms/event.py:157 +msgid "Your address" +msgstr "Deine Adresse" + +#: pretix/control/forms/event.py:158 +msgid "" +"Will be printed as the sender on invoices. Be sure to include relevant " +"details required in your jurisdiction (e.g. your VAT ID)." +msgstr "" +"Wird als Absender auf Rechnungen gedruckt. Denke daran, alle rechtlich " +"erforderlichen Merkmale aufzuführen, z.B. deine USt-ID oder Steuernummer." + +#: pretix/control/forms/event.py:163 +msgid "Additional text" +msgstr "Zusätzlicher Text" + +#: pretix/control/forms/event.py:164 +msgid "Will be printed on every invoice below the invoice total." +msgstr "Wird auf jeder Rechnung unterhalb des Gesamtbetrages gedruckt." + +#: pretix/control/forms/event.py:168 +msgid "Invoice language" +msgstr "Rechnungssprache" + +#: pretix/control/forms/event.py:169 +msgid "The user's language" +msgstr "Sprache des Benutzers" + +#: pretix/control/forms/event.py:173 msgid "Maximum number of items per order" msgstr "Maximale Anzahl Produkte pro Bestellung" -#: pretix/control/forms/event.py:152 +#: pretix/control/forms/event.py:177 msgid "Reservation period" msgstr "Reservierungszeitraum" -#: pretix/control/forms/event.py:153 +#: pretix/control/forms/event.py:178 msgid "" "The number of minutes the items in a user's card are reserved for this user." msgstr "" "Die Dauer in Minuten, die Produkte im Warenkorb eines Benutzers reserviert " "werden." -#: pretix/control/forms/event.py:156 +#: pretix/control/forms/event.py:181 msgid "Imprint URL" msgstr "Impressum (URL)" -#: pretix/control/forms/event.py:160 +#: pretix/control/forms/event.py:185 msgid "Contact address" msgstr "Kontakt-E-Mail" -#: pretix/control/forms/event.py:162 +#: pretix/control/forms/event.py:187 msgid "Public email address for contacting the organizer" msgstr "Öffentliche E-Mail-Adresse zur Kontaktierung des Veranstalters" -#: pretix/control/forms/event.py:169 +#: pretix/control/forms/event.py:194 msgid "" "Your default locale must also be enebled for your event (see box above)." msgstr "" "Die Standardsprache muss eine der aktivierten Sprachen sein (siehe weiter " "oben)." -#: pretix/control/forms/event.py:173 +#: pretix/control/forms/event.py:198 msgid "" "You cannot require specifying attendee names if you do not ask for them." msgstr "" "Du kannst die Angabe von Teilnehmernamen nur erfordern, wenn auch nach Namen " "gefragt wird." -#: pretix/control/forms/event.py:202 pretix/control/forms/event.py:304 +#: pretix/control/forms/event.py:227 pretix/control/forms/event.py:329 msgid "This field is required." msgstr "Dieses Feld ist erforderlich." -#: pretix/control/forms/event.py:207 +#: pretix/control/forms/event.py:232 msgid "Subject prefix" msgstr "Betreffs-Prefix" -#: pretix/control/forms/event.py:208 +#: pretix/control/forms/event.py:233 msgid "" "This will be prepended to the subject of all outgoing emails. This could be " "a short form of your event name." @@ -1314,19 +1512,19 @@ msgstr "" "Dies wird jedem E-Mail-Betreff vorangestellt und könnten z.B. eine Kurzform " "deines Veranstaltungsnamens enthalten." -#: pretix/control/forms/event.py:213 +#: pretix/control/forms/event.py:238 msgid "Sender address" msgstr "Absender-Adresse" -#: pretix/control/forms/event.py:214 +#: pretix/control/forms/event.py:239 msgid "Sender address for outgoing e-mails" msgstr "Absender-Adresse für ausgehende E-Mails" -#: pretix/control/forms/event.py:217 +#: pretix/control/forms/event.py:242 msgid "Placed order" msgstr "Getätigte Bestellung" -#: pretix/control/forms/event.py:220 +#: pretix/control/forms/event.py:245 #, python-brace-format msgid "" "Available placeholders: {event}, {total}, {currency}, {date}, {paymentinfo}, " @@ -1335,25 +1533,25 @@ msgstr "" "Verfügbare Platzhalter: {event}, {total}, {currency}, {date}, {paymentinfo}, " "{url}" -#: pretix/control/forms/event.py:223 +#: pretix/control/forms/event.py:248 msgid "Paid order" msgstr "Bezahlt Bestellung" -#: pretix/control/forms/event.py:226 pretix/control/forms/event.py:232 +#: pretix/control/forms/event.py:251 pretix/control/forms/event.py:257 #, python-brace-format msgid "Available placeholders: {event}, {url}" msgstr "Verfügbare Platzhalter: {event}, {url}" -#: pretix/control/forms/event.py:229 +#: pretix/control/forms/event.py:254 #: pretix/control/templates/pretixcontrol/order/index.html:76 msgid "Resend link" msgstr "Link erneut senden" -#: pretix/control/forms/event.py:235 +#: pretix/control/forms/event.py:260 msgid "Use custom SMTP server" msgstr "Eigenen SMTP-Server verwenden" -#: pretix/control/forms/event.py:236 +#: pretix/control/forms/event.py:261 msgid "" "All mail related to your event will be sent over the smtp server specified " "by you." @@ -1361,49 +1559,49 @@ msgstr "" "Alle E-Mails bezüglich deiner Veranstaltung werden über den von dir " "angegebenen SMTP-Server versendet." -#: pretix/control/forms/event.py:240 +#: pretix/control/forms/event.py:265 msgid "Hostname" msgstr "Hostname" -#: pretix/control/forms/event.py:244 +#: pretix/control/forms/event.py:269 msgid "Port" msgstr "Port" -#: pretix/control/forms/event.py:248 +#: pretix/control/forms/event.py:273 msgid "Username" msgstr "Benutzername" -#: pretix/control/forms/event.py:257 +#: pretix/control/forms/event.py:282 msgid "Use STARTTLS" msgstr "STARTTLS verwenden" -#: pretix/control/forms/event.py:258 +#: pretix/control/forms/event.py:283 msgid "Commonly enabled on port 587." msgstr "Meistens auf Port 587 verfügbar." -#: pretix/control/forms/event.py:262 +#: pretix/control/forms/event.py:287 msgid "Use SSL" msgstr "SSL verwenden" -#: pretix/control/forms/event.py:263 +#: pretix/control/forms/event.py:288 msgid "Commonly enabled on port 465." msgstr "Meistens auf Port 465 verfügbar." -#: pretix/control/forms/event.py:278 +#: pretix/control/forms/event.py:303 msgid "Use feature" msgstr "Funktion benutzen" -#: pretix/control/forms/event.py:279 +#: pretix/control/forms/event.py:304 msgid "Use pretix to generate tickets for the user to download and print out." msgstr "" "Wenn diese Funktion aktiviert ist, generiert pretix Tickets, die der " "Benutzer herunterladen und ausdrucken kann." -#: pretix/control/forms/event.py:283 +#: pretix/control/forms/event.py:308 msgid "Download date" msgstr "Download-Datum" -#: pretix/control/forms/event.py:284 +#: pretix/control/forms/event.py:309 msgid "Ticket download will be offered after this date." msgstr "Der Ticket-Download wird zu diesem Zeitpunkt freigeschaltet." @@ -1508,12 +1706,6 @@ msgstr "Alle Tickets" msgid "Filter" msgstr "Filter" -#: pretix/control/templates/pretixcontrol/attendees/index.html:35 -#: pretix/control/templates/pretixcontrol/order/index.html:60 -#: pretix/control/templates/pretixcontrol/orders/index.html:45 -msgid "Order date" -msgstr "Bestelldatum" - #: pretix/control/templates/pretixcontrol/auth/base.html:29 #: pretix/control/templates/pretixcontrol/base.html:126 #: pretix/presale/templates/pretixpresale/base.html:33 @@ -1551,7 +1743,7 @@ msgstr "Neues Passwort setzen" #: pretix/control/templates/pretixcontrol/event/mail.html:30 #: pretix/control/templates/pretixcontrol/event/payment.html:31 #: pretix/control/templates/pretixcontrol/event/permissions.html:56 -#: pretix/control/templates/pretixcontrol/event/settings.html:50 +#: pretix/control/templates/pretixcontrol/event/settings.html:58 #: pretix/control/templates/pretixcontrol/event/tickets.html:36 #: pretix/control/templates/pretixcontrol/events/create.html:30 #: pretix/control/templates/pretixcontrol/item/index.html:31 @@ -1631,7 +1823,7 @@ msgstr "Allgemeines" #: pretix/control/templates/pretixcontrol/event/base.html:31 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:51 #: pretix/presale/templates/pretixpresale/event/order.html:31 -#: pretix/presale/templates/pretixpresale/event/order.html:148 +#: pretix/presale/templates/pretixpresale/event/order.html:175 msgid "Payment" msgstr "Zahlung" @@ -1827,9 +2019,9 @@ msgstr "Anzeigeeinstellungen" msgid "Timeline" msgstr "Ablauf" -#: pretix/control/templates/pretixcontrol/event/settings.html:39 -msgid "Order process settings" -msgstr "Bestellzeile" +#: pretix/control/templates/pretixcontrol/event/settings.html:46 +msgid "Invoicing" +msgstr "Rechnungswesen" #: pretix/control/templates/pretixcontrol/event/tickets.html:8 #: pretix/presale/templates/pretixpresale/event/order.html:52 @@ -2110,7 +2302,7 @@ msgstr "Verbleibende Kapazität" #: pretix/control/templates/pretixcontrol/order/cancel.html:4 #: pretix/control/templates/pretixcontrol/order/cancel.html:8 #: pretix/control/templates/pretixcontrol/order/index.html:28 -#: pretix/presale/templates/pretixpresale/event/order.html:138 +#: pretix/presale/templates/pretixpresale/event/order.html:165 #: pretix/presale/templates/pretixpresale/event/order_cancel.html:4 msgid "Cancel order" msgstr "Stornieren" @@ -2168,70 +2360,87 @@ msgstr "Bestellung" msgid "Expiry date" msgstr "Ablaufdatum" -#: pretix/control/templates/pretixcontrol/order/index.html:86 +#: pretix/control/templates/pretixcontrol/order/index.html:81 +#: pretix/presale/templates/pretixpresale/event/order.html:100 +msgid "Invoices" +msgstr "Rechnungen" + +#: pretix/control/templates/pretixcontrol/order/index.html:85 +#: pretix/presale/templates/pretixpresale/event/order.html:108 +msgid "Cancellation" +msgstr "Rechnungskorrektur" + +#: pretix/control/templates/pretixcontrol/order/index.html:85 +#: pretix/presale/templates/pretixpresale/event/order.html:108 +msgid "Invoice" +msgstr "Rechnung" + +#: pretix/control/templates/pretixcontrol/order/index.html:99 #: pretix/presale/templates/pretixpresale/event/order.html:87 msgid "Ordered items" msgstr "Bestellte Produkte" -#: pretix/control/templates/pretixcontrol/order/index.html:98 +#: pretix/control/templates/pretixcontrol/order/index.html:111 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:11 msgid "Voucher code used:" msgstr "Verwendeter Gutscheincode:" -#: pretix/control/templates/pretixcontrol/order/index.html:108 -#: pretix/control/templates/pretixcontrol/order/index.html:113 +#: pretix/control/templates/pretixcontrol/order/index.html:121 +#: pretix/control/templates/pretixcontrol/order/index.html:126 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:17 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:21 msgid "not answered" msgstr "nicht beantwortet" -#: pretix/control/templates/pretixcontrol/order/index.html:128 +#: pretix/control/templates/pretixcontrol/order/index.html:141 +#: pretix/control/templates/pretixcontrol/order/index.html:158 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:63 +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:81 #: pretix/presale/templates/pretixpresale/event/index.html:104 #: pretix/presale/templates/pretixpresale/event/index.html:141 #, python-format msgid "incl. %(rate)s%% taxes" msgstr "inkl. %(rate)s%% MwSt." -#: pretix/control/templates/pretixcontrol/order/index.html:150 +#: pretix/control/templates/pretixcontrol/order/index.html:168 #: pretix/control/templates/pretixcontrol/orders/overview.html:85 #: pretix/plugins/reports/exporters.py:199 -#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:85 +#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:91 msgid "Total" msgstr "Gesamt" -#: pretix/control/templates/pretixcontrol/order/index.html:168 +#: pretix/control/templates/pretixcontrol/order/index.html:186 msgid "The payment state of this order was manually modified." msgstr "Der Status dieser Bestellung wurde manuell verändert." -#: pretix/control/templates/pretixcontrol/order/index.html:173 +#: pretix/control/templates/pretixcontrol/order/index.html:191 #, python-format msgid "The payment has to be completed before %(date)s." msgstr "Die Zahlung muss bis zum %(date)s abgeschlossen sein." -#: pretix/control/templates/pretixcontrol/order/index.html:183 +#: pretix/control/templates/pretixcontrol/order/index.html:201 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:70 #: pretix/presale/templates/pretixpresale/event/checkout_questions.html:30 -#: pretix/presale/templates/pretixpresale/event/order.html:106 +#: pretix/presale/templates/pretixpresale/event/order.html:130 #: pretix/presale/templates/pretixpresale/event/order_modify.html:19 msgid "Invoice information" msgstr "Rechnungsinformationen" -#: pretix/control/templates/pretixcontrol/order/index.html:188 +#: pretix/control/templates/pretixcontrol/order/index.html:206 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:75 -#: pretix/presale/templates/pretixpresale/event/order.html:111 +#: pretix/presale/templates/pretixpresale/event/order.html:135 msgid "Company" msgstr "Firma" -#: pretix/control/templates/pretixcontrol/order/index.html:194 +#: pretix/control/templates/pretixcontrol/order/index.html:212 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:81 -#: pretix/presale/templates/pretixpresale/event/order.html:117 +#: pretix/presale/templates/pretixpresale/event/order.html:141 msgid "ZIP code and city" msgstr "PLZ und Ort" -#: pretix/control/templates/pretixcontrol/order/index.html:198 +#: pretix/control/templates/pretixcontrol/order/index.html:216 #: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:85 -#: pretix/presale/templates/pretixpresale/event/order.html:121 +#: pretix/presale/templates/pretixpresale/event/order.html:145 msgid "Phone" msgstr "Telefon" @@ -2564,39 +2773,51 @@ msgstr "Das ausgewählte Produkt wurde deaktiviert." msgid "The new event has been created." msgstr "Eine neue Veranstaltung wurde erstellt." -#: pretix/control/views/orders.py:172 +#: pretix/control/views/orders.py:174 msgid "The order has been marked as paid." msgstr "Die Bestellung wurde als bezahlt markiert." -#: pretix/control/views/orders.py:175 +#: pretix/control/views/orders.py:177 msgid "The order has been cancelled." msgstr "Die Bestellung wurde storniert." -#: pretix/control/views/orders.py:181 +#: pretix/control/views/orders.py:183 msgid "The order has been marked as not paid." msgstr "Die Bestellung wurde als unbezahlt markiert." -#: pretix/control/views/orders.py:219 +#: pretix/control/views/orders.py:221 msgid "The email has been queued to be sent." msgstr "Die E-Mail wurde zum Versenden gespeichert." -#: pretix/control/views/orders.py:236 pretix/presale/views/order.py:294 +#: pretix/control/views/orders.py:243 pretix/presale/views/order.py:341 +msgid "This invoice has not been found" +msgstr "Diese Rechnung wurde nicht gefunden" + +#: pretix/control/views/orders.py:251 pretix/presale/views/order.py:349 +msgid "" +"The invoice file has not yet been generated, we will generate it for you " +"now. Please try again in a few seconds." +msgstr "" +"Diese Rechnung wurde bisher noch nicht fertig erstellt, wir werden die PDF-" +"Datei jetzt erstellen. Bitte probiere es in wenigen Sekunden erneut." + +#: pretix/control/views/orders.py:270 pretix/presale/views/order.py:298 msgid "You requested an invalid ticket output type." msgstr "Du hast einen ungültigen Ausgabetyp gewählt." -#: pretix/control/views/orders.py:239 pretix/presale/views/order.py:299 +#: pretix/control/views/orders.py:273 pretix/presale/views/order.py:303 msgid "Order is not paid." msgstr "Bestellung ist nicht bezahlt." -#: pretix/control/views/orders.py:265 pretix/control/views/orders.py:303 +#: pretix/control/views/orders.py:299 pretix/control/views/orders.py:337 msgid "This action is only allowed for pending orders." msgstr "Diese Aktion ist nur für unbezahlte Bestellungen möglich." -#: pretix/control/views/orders.py:271 pretix/control/views/orders.py:285 +#: pretix/control/views/orders.py:305 pretix/control/views/orders.py:319 msgid "The payment term has been changed." msgstr "Die Zahlungsfrist wurde geändert." -#: pretix/control/views/orders.py:289 +#: pretix/control/views/orders.py:323 msgid "" "We were not able to process the request completely as the server was too " "busy." @@ -2604,15 +2825,15 @@ msgstr "" "Wir konnten deine Bestellung nicht durchführen, da der Server zu beschäftigt " "war." -#: pretix/control/views/orders.py:338 +#: pretix/control/views/orders.py:372 msgid "There is no order with the given order code." msgstr "Es existiert keine Bestellung mit der eingegebenen Bestellnummer." -#: pretix/control/views/orders.py:372 +#: pretix/control/views/orders.py:406 msgid "The selected exporter was not found." msgstr "Das ausgewählte Exportformat wurde nicht gefunden." -#: pretix/control/views/orders.py:378 +#: pretix/control/views/orders.py:412 msgid "There was a problem processing your input. See below for error details." msgstr "Die Eingabe konnte nicht verarbeitet werden." @@ -3138,11 +3359,6 @@ msgstr "" "Dieses Plugin erlaubt, ausdruckbare Berichte über deine Verkäufe zu " "erstellen." -#: pretix/plugins/reports/exporters.py:97 -#, python-format -msgid "Page %d" -msgstr "Seite %d" - #: pretix/plugins/reports/exporters.py:99 #, python-format msgid "Created: %s" @@ -3477,7 +3693,7 @@ msgstr "Veranstaltungsname-y-Position (mm)" msgid "Event name size (mm)" msgstr "Veranstaltungsname-Größe (mm)" -#: pretix/presale/checkoutflow.py:166 pretix/presale/views/order.py:230 +#: pretix/presale/checkoutflow.py:166 pretix/presale/views/order.py:234 msgid "" "We had difficulties processing your input. Please review the errors below." msgstr "Wir hatten Schwierigkeiten, deine Eingabe zu verarbeiten." @@ -3495,15 +3711,15 @@ msgid "Please select a payment method." msgstr "Bitte wähle eine Zahlungsmethode aus." #: pretix/presale/checkoutflow.py:274 pretix/presale/checkoutflow.py:280 -#: pretix/presale/views/order.py:155 pretix/presale/views/order.py:184 +#: pretix/presale/views/order.py:159 pretix/presale/views/order.py:188 msgid "The payment information you entered was incomplete." msgstr "Die eingegebenen Zahlungsinformationen sind unvollständig." -#: pretix/presale/forms/checkout.py:19 +#: pretix/presale/forms/checkout.py:21 msgid "Street and Number" msgstr "Straße und Hausnummer" -#: pretix/presale/forms/checkout.py:37 +#: pretix/presale/forms/checkout.py:39 msgid "You need to provide either a company name or your name." msgstr "Du musst entweder einen Firmennamen oder deinen Namen eingeben." @@ -3713,7 +3929,7 @@ msgid "You will be able to download your tickets here on %(date)s." msgstr "Du kannst deine Tickets hier ab %(date)s herunterladen." #: pretix/presale/templates/pretixpresale/event/order.html:82 -#: pretix/presale/templates/pretixpresale/event/order.html:101 +#: pretix/presale/templates/pretixpresale/event/order.html:125 msgid "Change details" msgstr "Details bearbeiten" @@ -3819,27 +4035,27 @@ msgstr "Die gewählten Produkte wurden deinem Warenkorb hinzugefügt." msgid "Your cart is empty" msgstr "Dein Warenkorb ist leer." -#: pretix/presale/views/order.py:55 pretix/presale/views/order.py:107 -#: pretix/presale/views/order.py:149 pretix/presale/views/order.py:181 -#: pretix/presale/views/order.py:243 pretix/presale/views/order.py:264 -#: pretix/presale/views/order.py:297 +#: pretix/presale/views/order.py:58 pretix/presale/views/order.py:111 +#: pretix/presale/views/order.py:153 pretix/presale/views/order.py:185 +#: pretix/presale/views/order.py:247 pretix/presale/views/order.py:268 +#: pretix/presale/views/order.py:301 pretix/presale/views/order.py:332 msgid "Unknown order code or not authorized to access this order." msgstr "" "Unbekannte Bestellnummer oder Bestellung gehört einem anderen Benutzer." -#: pretix/presale/views/order.py:111 pretix/presale/views/order.py:151 +#: pretix/presale/views/order.py:115 pretix/presale/views/order.py:155 msgid "The payment for this order cannot be continued." msgstr "Diese Bestellung kann nicht mehr bezahlt werden." -#: pretix/presale/views/order.py:245 +#: pretix/presale/views/order.py:249 msgid "You cannot modify this order" msgstr "Du kannst diese Bestellung nicht bearbeiten" -#: pretix/presale/views/order.py:266 +#: pretix/presale/views/order.py:270 msgid "You cannot cancel this order" msgstr "Du kannst diese Bestellung nicht stornieren" -#: pretix/presale/views/order.py:304 +#: pretix/presale/views/order.py:308 msgid "Ticket download is not (yet) enabled." msgstr "Der Ticket-Download ist (noch) nicht freigeschaltet." @@ -3855,6 +4071,9 @@ msgstr "Deutsch" msgid "German (informal)" msgstr "Deutsch (Du)" +#~ msgid "Order process settings" +#~ msgstr "Bestellzeile" + #~ msgid "Tickets sold" #~ msgstr "Tickets verkauft" @@ -3951,9 +4170,6 @@ msgstr "Deutsch (Du)" #~ msgid "Value" #~ msgstr "Wert" -#~ msgid "Property value" -#~ msgstr "Wert" - #~ msgid "Property values" #~ msgstr "Werte" @@ -4119,9 +4335,6 @@ msgstr "Deutsch (Du)" #~ msgid "Welcome back!" #~ msgstr "Willkommen zurück!" -#~ msgid "Your orders" -#~ msgstr "Deine Bestellungen" - #~ msgid "Place new order" #~ msgstr "Neue Bestellung" diff --git a/src/pretix/presale/templates/pretixpresale/event/order.html b/src/pretix/presale/templates/pretixpresale/event/order.html index 015dde17f5..51d1c7b9e2 100644 --- a/src/pretix/presale/templates/pretixpresale/event/order.html +++ b/src/pretix/presale/templates/pretixpresale/event/order.html @@ -91,43 +91,70 @@ {% include "pretixpresale/event/fragment_cart.html" with cart=cart event=request.event editable=False %} - {% if request.event.settings.invoice_address_asked %} -
-
- {% if order.can_modify_answers %} -
- - - {% trans "Change details" %} - +
+ {% if invoices %} +
+
+
+

+ {% trans "Invoices" %} +

- {% endif %} -

- {% trans "Invoice information" %} -

+
+ +
+
-
-
-
{% trans "Company" %}
-
{{ order.invoice_address.company }}
-
{% trans "Name" %}
-
{{ order.invoice_address.name }}
-
{% trans "Address" %}
-
{{ order.invoice_address.street|linebreaksbr }}
-
{% trans "ZIP code and city" %}
-
{{ order.invoice_address.zipcode }} {{ addr.city }}
-
{% trans "Country" %}
-
{{ order.invoice_address.country }}
-
{% trans "Phone" %}
-
{{ order.invoice_address.phone }}
- {% if request.event.settings.invoice_address_vatid %} -
{% trans "VAT ID" %}
-
{{ order.invoice_address.vat_id }}
- {% endif %} -
+ {% endif %} + {% if request.event.settings.invoice_address_asked %} +
+
+
+ {% if order.can_modify_answers %} + + {% endif %} +

+ {% trans "Invoice information" %} +

+
+
+
+
{% trans "Company" %}
+
{{ order.invoice_address.company }}
+
{% trans "Name" %}
+
{{ order.invoice_address.name }}
+
{% trans "Address" %}
+
{{ order.invoice_address.street|linebreaksbr }}
+
{% trans "ZIP code and city" %}
+
{{ order.invoice_address.zipcode }} {{ addr.city }}
+
{% trans "Country" %}
+
{{ order.invoice_address.country }}
+
{% trans "Phone" %}
+
{{ order.invoice_address.phone }}
+ {% if request.event.settings.invoice_address_vatid %} +
{% trans "VAT ID" %}
+
{{ order.invoice_address.vat_id }}
+ {% endif %} +
+
+
-
- {% endif %} + {% endif %} +
+
{% if order.status == "n" %}
diff --git a/src/pretix/presale/urls.py b/src/pretix/presale/urls.py index 419bb12354..0ee8e7cb09 100644 --- a/src/pretix/presale/urls.py +++ b/src/pretix/presale/urls.py @@ -35,6 +35,9 @@ event_patterns = [ url(r'^order/(?P[^/]+)/(?P[A-Za-z0-9]+)/download/(?P[^/]+)$', pretix.presale.views.order.OrderDownload.as_view(), name='event.order.download'), + url(r'^order/(?P[^/]+)/(?P[A-Za-z0-9]+)/invoice/(?P[^/]+)$', + pretix.presale.views.order.InvoiceDownload.as_view(), + name='event.invoice.download'), url(r'^$', pretix.presale.views.event.EventIndex.as_view(), name='event.index'), ] diff --git a/src/pretix/presale/views/order.py b/src/pretix/presale/views/order.py index d2537d572c..d80423fcc7 100644 --- a/src/pretix/presale/views/order.py +++ b/src/pretix/presale/views/order.py @@ -9,8 +9,11 @@ from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ from django.views.generic import TemplateView, View -from pretix.base.models import CachedFile, CachedTicket, Order, OrderPosition +from pretix.base.models import ( + CachedFile, CachedTicket, Invoice, Order, OrderPosition, +) from pretix.base.models.orders import InvoiceAddress +from pretix.base.services.invoices import invoice_pdf from pretix.base.services.orders import cancel_order from pretix.base.services.tickets import generate from pretix.base.signals import ( @@ -85,6 +88,7 @@ class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TemplateView): answers=True, queryset=OrderPosition.objects.filter(order=self.order) ) + ctx['invoices'] = list(self.order.invoices.all()) if self.order.status == Order.STATUS_PENDING: ctx['payment'] = self.payment_provider.order_pending_render(self.request, self.order) ctx['can_retry'] = ( @@ -319,3 +323,31 @@ class OrderDownload(EventViewMixin, OrderDetailMixin, View): ct.save() generate(self.order.id, self.output.identifier) return redirect(reverse('cachedfile.download', kwargs={'id': ct.cachedfile.id})) + + +class InvoiceDownload(EventViewMixin, OrderDetailMixin, View): + + def get(self, request, *args, **kwargs): + if not self.order: + raise Http404(_('Unknown order code or not authorized to access this order.')) + + try: + invoice = Invoice.objects.get( + event=self.request.event, + order=self.order, + id=self.kwargs['invoice'] + ) + except Invoice.DoesNotExist: + raise Http404(_('This invoice has not been found')) + + if not invoice.file: + invoice_pdf(invoice.pk) + invoice = Invoice.objects.get(pk=invoice.pk) + + if not invoice.file: + # This happens if we have celery installed and the file will be generated in the background + messages.warning(request, _('The invoice file has not yet been generated, we will generate it for you ' + 'now. Please try again in a few seconds.')) + return redirect(self.get_order_url()) + + return redirect(invoice.file.url) diff --git a/src/pretix/settings.py b/src/pretix/settings.py index c496f0c766..533374fd2c 100644 --- a/src/pretix/settings.py +++ b/src/pretix/settings.py @@ -204,11 +204,11 @@ LOCALE_PATHS = ( os.path.join(os.path.dirname(__file__), 'locale'), ) -LANGUAGES = ( +LANGUAGES = [ ('en', _('English')), ('de', _('German')), ('de-informal', _('German (informal)')), -) +] EXTRA_LANG_INFO = { 'de-informal': {