diff --git a/.gitea/workflows/cicd.yaml b/.gitea/workflows/cicd.yaml new file mode 100644 index 000000000..fe5c81d1c --- /dev/null +++ b/.gitea/workflows/cicd.yaml @@ -0,0 +1,31 @@ +name: Build Deploy email notification tool +run-name: ${{ gitea.actor }} building new version of the email notification tool +on: + push: # Baut bei jedem Push (Branches + Tags) + workflow_dispatch: + +jobs: + Apply-Kubernetes-Resources: + runs-on: podman + steps: + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Login to Docker Registry + run: podman login -u ${{ secrets.REGISTRY_USERNAME }} -p ${{ secrets.REGISTRY_TOKEN }} cr.ortlerstrasse.de + + - name: Set Docker Image Tag + run: | + if [[ "${{ gitea.ref }}" == refs/tags/* ]]; then + echo "TAG_NAME=${{ gitea.ref_name }}" >> $GITHUB_ENV + else + echo "TAG_NAME=latest" >> $GITHUB_ENV + fi + + - name: Build Docker image + run: podman build -t cr.ortlerstrasse.de/cgo/pretix:${{ env.TAG_NAME }} . + + - name: Push Docker image + run: | + podman push cr.ortlerstrasse.de/cgo/pretix:${{ env.TAG_NAME }} + echo "Image pushed successfully: cr.ortlerstrasse.de/cgo/pretix:${{ env.TAG_NAME }}" \ No newline at end of file diff --git a/src/pretix/base/exporters/__init__.py b/src/pretix/base/exporters/__init__.py index 10671ee8f..4260f07fe 100644 --- a/src/pretix/base/exporters/__init__.py +++ b/src/pretix/base/exporters/__init__.py @@ -28,5 +28,6 @@ from .items import * # noqa from .json import * # noqa from .mail import * # noqa from .orderlist import * # noqa +from .relevant_orderlist import * # noqa from .reusablemedia import * # noqa from .waitinglist import * # noqa diff --git a/src/pretix/base/exporters/orderlist.py b/src/pretix/base/exporters/orderlist.py index 945737d35..c0756ca8a 100644 --- a/src/pretix/base/exporters/orderlist.py +++ b/src/pretix/base/exporters/orderlist.py @@ -89,7 +89,7 @@ class OrderListExporter(MultiSheetListExporter): description = gettext_lazy('Download a spreadsheet of all orders. The spreadsheet will include three sheets, one ' 'with a line for every order, one with a line for every order position, and one with ' 'a line for every additional fee charged in an order.') - featured = True + featured = False repeatable_read = False @cached_property diff --git a/src/pretix/base/exporters/relevant_orderlist.py b/src/pretix/base/exporters/relevant_orderlist.py new file mode 100644 index 000000000..a0e2355f7 --- /dev/null +++ b/src/pretix/base/exporters/relevant_orderlist.py @@ -0,0 +1,1199 @@ +# +# This file is part of pretix (Community Edition). +# +# Copyright (C) 2014-2020 Raphael Michel and contributors +# Copyright (C) 2020-2021 rami.io GmbH and contributors +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General +# Public License as published by the Free Software Foundation in version 3 of the License. +# +# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are +# applicable granting you additional permissions and placing additional restrictions on your usage of this software. +# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive +# this file, see . +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along with this program. If not, see +# . +# + +# This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of +# the Apache License 2.0 can be obtained at . +# +# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A +# full history of changes and contributors is available at . +# +# This file contains Apache-licensed contributions copyrighted by: Benjamin Hättasch, Tobias Kunze +# +# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under the License. + +from collections import OrderedDict, defaultdict +from decimal import Decimal +from zoneinfo import ZoneInfo + +from django import forms +from django.conf import settings +from django.db.models import ( + Case, + CharField, + Count, + DateTimeField, + F, + IntegerField, + Max, + Min, + OuterRef, + Q, + Subquery, + Sum, + When, +) +from django.db.models.functions import Coalesce +from django.dispatch import receiver +from django.utils.formats import date_format +from django.utils.functional import cached_property +from django.utils.timezone import get_current_timezone, now +from django.utils.translation import ( + gettext as _, + gettext_lazy, + pgettext, + pgettext_lazy, +) +from openpyxl.cell import WriteOnlyCell +from openpyxl.comments import Comment +from openpyxl.styles import Font, PatternFill + +from pretix.base.models import ( + Checkin, + GiftCard, + GiftCardTransaction, + Invoice, + InvoiceAddress, + Order, + OrderPosition, + Question, +) +from pretix.base.models.orders import ( + OrderFee, + OrderPayment, + OrderRefund, + Transaction, +) +from pretix.base.services.quotas import QuotaAvailability +from pretix.base.settings import PERSON_NAME_SCHEMES, get_name_parts_localized + +from ...control.forms.filter import get_all_payment_providers +from ...helpers import GroupConcat +from ...helpers.iter import chunked_iterable +from ...helpers.safe_openpyxl import remove_invalid_excel_chars +from ..exporter import ( + ListExporter, + MultiSheetListExporter, + OrganizerLevelExportMixin, +) +from ..forms.widgets import SplitDateTimePickerWidget +from ..signals import ( + register_data_exporters, + register_multievent_data_exporters, +) +from ..timeframes import ( + DateFrameField, + resolve_timeframe_to_datetime_start_inclusive_end_exclusive, +) + + +class RelevantOrderListExporter(MultiSheetListExporter): + identifier = "relevantorderlist" + verbose_name = gettext_lazy("Order data (sorted by relevance)") + category = pgettext_lazy("export_category", "Order data") + description = gettext_lazy( + "Download a spreadsheet of all orders. The spreadsheet will include three sheets, one " + "with a line for every order, one with a line for every order position, and one with " + "a line for every additional fee charged in an order. The most relevant data is in the " + "first columns of the tables." + ) + featured = True + + @cached_property + def providers(self): + return dict(get_all_payment_providers()) + + @property + def sheets(self): + return ( + ("orders", _("Orders")), + ("positions", _("Order positions")), + ("fees", _("Order fees")), + ) + + @property + def additional_form_fields(self): + d = [ + ( + "paid_only", + forms.BooleanField( + label=_("Only paid orders"), initial=False, required=False + ), + ), + ( + "include_payment_amounts", + forms.BooleanField( + label=_("Include payment amounts"), initial=True, required=False + ), + ), + ( + "group_multiple_choice", + forms.BooleanField( + label=_("Show multiple choice answers grouped in one column"), + initial=False, + required=False, + ), + ), + ( + "date_range", + DateFrameField( + label=_("Date range"), + include_future_frames=False, + required=False, + help_text=_("Only include orders created within this date range."), + ), + ), + ( + "event_date_range", + DateFrameField( + label=_("Event date"), + include_future_frames=True, + required=False, + help_text=_( + "Only include orders including at least one ticket for a date in this range. " + "Will also include other dates in case of mixed orders!" + ), + ), + ), + ] + d = OrderedDict(d) + if not self.is_multievent and not self.event.has_subevents: + del d["event_date_range"] + return d + + def _get_all_payment_methods(self, qs): + pps = dict(get_all_payment_providers()) + return sorted( + [ + (pp, pps[pp]) + for pp in set( + OrderPayment.objects.exclude(provider="free") + .filter(order__event__in=self.events) + .values_list("provider", flat=True) + .distinct() + ) + ], + key=lambda pp: pp[0], + ) + + def _get_all_tax_rates(self, qs): + tax_rates = set( + a + for a in OrderFee.objects.filter(order__event__in=self.events) + .values_list("tax_rate", flat=True) + .distinct() + .order_by() + ) + tax_rates |= set( + a + for a in OrderPosition.objects.filter(order__event__in=self.events) + .values_list("tax_rate", flat=True) + .distinct() + .order_by() + ) + tax_rates = sorted(tax_rates) + return tax_rates + + def iterate_sheet(self, form_data, sheet): + if sheet == "orders": + return self.iterate_orders(form_data) + elif sheet == "positions": + return self.iterate_positions(form_data) + elif sheet == "fees": + return self.iterate_fees(form_data) + + @cached_property + def event_object_cache(self): + return {e.pk: e for e in self.events} + + def _date_filter(self, qs, form_data, rel): + annotations = {} + filters = {} + + if form_data.get("date_range"): + dt_start, dt_end = ( + resolve_timeframe_to_datetime_start_inclusive_end_exclusive( + now(), form_data["date_range"], self.timezone + ) + ) + if dt_start: + filters[f"{rel}datetime__gte"] = dt_start + if dt_end: + filters[f"{rel}datetime__lt"] = dt_end + + if form_data.get("event_date_range"): + dt_start, dt_end = ( + resolve_timeframe_to_datetime_start_inclusive_end_exclusive( + now(), form_data["event_date_range"], self.timezone + ) + ) + if dt_start: + annotations["event_date_max"] = Case( + When( + **{f"{rel}event__has_subevents": True}, + then=Max(f"{rel}all_positions__subevent__date_from"), + ), + default=F(f"{rel}event__date_from"), + ) + filters["event_date_max__gte"] = dt_start + if dt_end: + annotations["event_date_min"] = Case( + When( + **{f"{rel}event__has_subevents": True}, + then=Min(f"{rel}all_positions__subevent__date_from"), + ), + default=F(f"{rel}event__date_from"), + ) + filters["event_date_min__lt"] = dt_end + + if filters: + return qs.annotate(**annotations).filter(**filters) + return qs + + def orders_qs(self, form_data): + p_date = ( + OrderPayment.objects.filter( + order=OuterRef("pk"), + state__in=( + OrderPayment.PAYMENT_STATE_CONFIRMED, + OrderPayment.PAYMENT_STATE_REFUNDED, + ), + payment_date__isnull=False, + ) + .values("order") + .annotate(m=Max("payment_date")) + .values("m") + .order_by() + ) + p_providers = ( + OrderPayment.objects.filter( + order=OuterRef("pk"), + state__in=( + OrderPayment.PAYMENT_STATE_CONFIRMED, + OrderPayment.PAYMENT_STATE_REFUNDED, + OrderPayment.PAYMENT_STATE_PENDING, + OrderPayment.PAYMENT_STATE_CREATED, + ), + ) + .values("order") + .annotate(m=GroupConcat("provider", delimiter=",")) + .values("m") + .order_by() + ) + i_numbers = ( + Invoice.objects.filter( + order=OuterRef("pk"), + ) + .values("order") + .annotate(m=GroupConcat("full_invoice_no", delimiter=", ")) + .values("m") + .order_by() + ) + + s = ( + OrderPosition.objects.filter(order=OuterRef("pk")) + .order_by() + .values("order") + .annotate(k=Count("id")) + .values("k") + ) + qs = ( + Order.objects.filter(event__in=self.events) + .annotate( + payment_date=Subquery(p_date, output_field=DateTimeField()), + payment_providers=Subquery(p_providers, output_field=CharField()), + invoice_numbers=Subquery(i_numbers, output_field=CharField()), + pcnt=Subquery(s, output_field=IntegerField()), + ) + .select_related("invoice_address", "customer") + ) + + qs = self._date_filter(qs, form_data, rel="") + + if form_data["paid_only"]: + qs = qs.filter(status=Order.STATUS_PAID) + return qs + + # Bestellungen (hier geht es hauptsächlich um die Beträge und ob sie bezahlt sind) + # Wichtig: Bestellnummer ("Order code"), Gesamtbetrag ("Order total"), Status ("Status"), + # E-Mail ("Email"), Name ("Name") + aufgeteilt, + # Bezahlt mit {Zahlungsmethode...} ("Payed by {method}") + # Semi-Wichtig: Datum der letzten Zahlung ("Date of last payment"), + # Zahlungsmethoden ("Payment providers") + # Unwichtig: alles andere + def iterate_orders(self, form_data: dict): + qs = self.orders_qs(form_data) + tax_rates = self._get_all_tax_rates(qs) + + # HEADERS START (Must be reordered the same way the Fields get reordered) + headers = [ + _("Order code"), # Wichtig + _("Order total"), # Wichtig + _("Status"), # Wichtig + _("Email"), # Wichtig + _("Name"), # Wichtig + ] + name_scheme = ( # für Wichtig + PERSON_NAME_SCHEMES[self.event.settings.name_scheme] + if not self.is_multievent + else None + ) + if name_scheme and len(name_scheme["fields"]) > 1: # Wichtig + for k, label, w in name_scheme["fields"]: + headers.append(label) + if form_data.get("include_payment_amounts"): # Wichtig + payment_methods = self._get_all_payment_methods(qs) + for id, vn in payment_methods: + headers.append(_("Paid by {method}").format(method=vn)) + + headers += [ + _("Date of last payment"), # Semi-Wichtig + _("Payment providers"), # Semi-Wichtig + _("Event slug"), + _("Event name"), + _("Phone number"), + _("Order date"), + _("Order time"), + _("Company"), + ] + headers += [ + _("Address"), + _("ZIP code"), + _("City"), + _("Country"), + pgettext("address", "State"), + _("Custom address field"), + _("VAT ID"), + _("Fees"), + _("Order locale"), + ] + + for tr in tax_rates: + headers += [ + _("Gross at {rate} % tax").format(rate=tr), + _("Net at {rate} % tax").format(rate=tr), + _("Tax value at {rate} % tax").format(rate=tr), + ] + + headers.append(_("Invoice numbers")) + headers.append(_("Sales channel")) + headers.append(_("Requires special attention")) + headers.append(_("Check-in text")) + headers.append(_("Comment")) + headers.append(_("Follow-up date")) + headers.append(_("Positions")) + headers.append(_("Email address verified")) + headers.append(_("External customer ID")) + + # get meta_data labels from first cached event + headers += next(iter(self.event_object_cache.values())).meta_data.keys() + yield headers + # HEADERS END + + full_fee_sum_cache = { + o["order__id"]: o["grosssum"] + for o in OrderFee.objects.values("tax_rate", "order__id") + .order_by() + .annotate(grosssum=Sum("value")) + } + fee_sum_cache = { + (o["order__id"], o["tax_rate"]): o + for o in OrderFee.objects.values("tax_rate", "order__id") + .order_by() + .annotate(taxsum=Sum("tax_value"), grosssum=Sum("value")) + } + if form_data.get("include_payment_amounts"): + payment_sum_cache = { + (o["order__id"], o["provider"]): o["grosssum"] + for o in OrderPayment.objects.values("provider", "order__id") + .order_by() + .filter( + state__in=[ + OrderPayment.PAYMENT_STATE_CONFIRMED, + OrderPayment.PAYMENT_STATE_REFUNDED, + ] + ) + .annotate(grosssum=Sum("amount")) + } + refund_sum_cache = { + (o["order__id"], o["provider"]): o["grosssum"] + for o in OrderRefund.objects.values("provider", "order__id") + .order_by() + .filter( + state__in=[ + OrderRefund.REFUND_STATE_DONE, + OrderRefund.REFUND_STATE_TRANSIT, + ] + ) + .annotate(grosssum=Sum("amount")) + } + sum_cache = { + (o["order__id"], o["tax_rate"]): o + for o in OrderPosition.objects.values("tax_rate", "order__id") + .order_by() + .annotate(taxsum=Sum("tax_value"), grosssum=Sum("price")) + } + + yield self.ProgressSetTotal(total=qs.count()) + for order in qs.order_by("datetime").iterator(): + tz = ZoneInfo(self.event_object_cache[order.event_id].settings.timezone) + + # ROW(S) START (Must be reordered the same way the Fields get reordered) + row = [ + order.code, # Wichtig ("Order code") + order.total, # Wichtig ("Order total") + order.get_extended_status_display(), # Wichtig ("Status") + order.email, # Wichtig ("Email") + ] + try: # Wichtig: ("Name") + aufgegeilt + row.append(order.invoice_address.name) + if name_scheme and len(name_scheme["fields"]) > 1: + for k, label, w in name_scheme["fields"]: + row.append( + get_name_parts_localized( + order.invoice_address.name_parts, k + ) + ) + except InvoiceAddress.DoesNotExist: + row += [""] * ( + 1 + + ( + len(name_scheme["fields"]) + if name_scheme and len(name_scheme["fields"]) > 1 + else 0 + ) + ) + + if form_data.get( + "include_payment_amounts" + ): # Wichtig ("Payed by {method}") + payment_methods = self._get_all_payment_methods(qs) + for id, vn in payment_methods: + row.append( + payment_sum_cache.get((order.id, id), Decimal("0.00")) + - refund_sum_cache.get((order.id, id), Decimal("0.00")) + ) + + row.append( # Semi-Wichtig ("Date of last payment") + order.payment_date.astimezone(tz).strftime("%Y-%m-%d") + if order.payment_date + else "" + ) + row.append( # Semi-Wichtig ("Payment providers") + ", ".join( + [ + str(self.providers.get(p, p)) + for p in sorted(set((order.payment_providers or "").split(","))) + if p and p != "free" + ] + ) + ) + + row += [ # unwichtig + self.event_object_cache[order.event_id].slug, + str(self.event_object_cache[order.event_id].name), + str(order.phone) if order.phone else "", + order.datetime.astimezone(tz).strftime("%Y-%m-%d"), + order.datetime.astimezone(tz).strftime("%H:%M:%S"), + ] + try: + row.append(order.invoice_address.company) + row += [ + order.invoice_address.street, + order.invoice_address.zipcode, + order.invoice_address.city, + ( + order.invoice_address.country + if order.invoice_address.country + else order.invoice_address.country_old + ), + order.invoice_address.state, + order.invoice_address.custom_field, + order.invoice_address.vat_id, + ] + except InvoiceAddress.DoesNotExist: + row += [""] * (8) + + row += [ + full_fee_sum_cache.get(order.id) or Decimal("0.00"), + order.locale, + ] + + for tr in tax_rates: + taxrate_values = sum_cache.get( + (order.id, tr), + {"grosssum": Decimal("0.00"), "taxsum": Decimal("0.00")}, + ) + fee_taxrate_values = fee_sum_cache.get( + (order.id, tr), + {"grosssum": Decimal("0.00"), "taxsum": Decimal("0.00")}, + ) + + row += [ + taxrate_values["grosssum"] + fee_taxrate_values["grosssum"], + ( + taxrate_values["grosssum"] + - taxrate_values["taxsum"] + + fee_taxrate_values["grosssum"] + - fee_taxrate_values["taxsum"] + ), + taxrate_values["taxsum"] + fee_taxrate_values["taxsum"], + ] + + row.append(order.invoice_numbers) + row.append(order.sales_channel) + row.append(_("Yes") if order.checkin_attention else _("No")) + row.append(order.checkin_text or "") + row.append(order.comment or "") + row.append( + order.custom_followup_at.strftime("%Y-%m-%d") + if order.custom_followup_at + else "" + ) + row.append(order.pcnt) + row.append(_("Yes") if order.email_known_to_work else _("No")) + row.append( + str(order.customer.external_identifier) + if order.customer and order.customer.external_identifier + else "" + ) + + row += self.event_object_cache[order.event_id].meta_data.values() + yield row + # ROW(S) END + + def fees_qs(self, form_data): + p_providers = ( + OrderPayment.objects.filter( + order=OuterRef("order"), + state__in=( + OrderPayment.PAYMENT_STATE_CONFIRMED, + OrderPayment.PAYMENT_STATE_REFUNDED, + OrderPayment.PAYMENT_STATE_PENDING, + OrderPayment.PAYMENT_STATE_CREATED, + ), + ) + .values("order") + .annotate(m=GroupConcat("provider", delimiter=",")) + .values("m") + .order_by() + ) + qs = ( + OrderFee.all.filter( + order__event__in=self.events, + ) + .annotate( + payment_providers=Subquery(p_providers, output_field=CharField()), + ) + .select_related( + "order", "order__invoice_address", "order__customer", "tax_rule" + ) + ) + if form_data["paid_only"]: + qs = qs.filter(order__status=Order.STATUS_PAID, canceled=False) + + qs = self._date_filter(qs, form_data, rel="order__") + return qs + + # Gebühren, hier sind alle Felder irrelevant + def iterate_fees(self, form_data: dict): + qs = self.fees_qs(form_data) + + headers = [ + _("Event slug"), + _("Event name"), + _("Order code"), + _("Status"), + _("Email"), + _("Phone number"), + _("Order date"), + _("Order time"), + _("Fee type"), + _("Description"), + _("Price"), + _("Tax rate"), + _("Tax rule"), + _("Tax value"), + _("Company"), + _("Invoice address name"), + ] + name_scheme = ( + PERSON_NAME_SCHEMES[self.event.settings.name_scheme] + if not self.is_multievent + else None + ) + if name_scheme and len(name_scheme["fields"]) > 1: + for k, label, w in name_scheme["fields"]: + headers.append(_("Invoice address name") + ": " + str(label)) + headers += [ + _("Address"), + _("ZIP code"), + _("City"), + _("Country"), + pgettext("address", "State"), + _("VAT ID"), + ] + + headers.append(_("External customer ID")) + headers.append(_("Payment providers")) + + # get meta_data labels from first cached event + headers += next(iter(self.event_object_cache.values())).meta_data.keys() + yield headers + + yield self.ProgressSetTotal(total=qs.count()) + for op in qs.order_by("order__datetime").iterator(): + order = op.order + tz = ZoneInfo(order.event.settings.timezone) + row = [ + self.event_object_cache[order.event_id].slug, + str(self.event_object_cache[order.event_id].name), + order.code, + _("canceled") if op.canceled else order.get_extended_status_display(), + order.email, + str(order.phone) if order.phone else "", + order.datetime.astimezone(tz).strftime("%Y-%m-%d"), + order.datetime.astimezone(tz).strftime("%H:%M:%S"), + op.get_fee_type_display(), + op.description, + op.value, + op.tax_rate, + str(op.tax_rule) if op.tax_rule else "", + op.tax_value, + ] + try: + row += [ + order.invoice_address.company, + order.invoice_address.name, + ] + if name_scheme and len(name_scheme["fields"]) > 1: + for k, label, w in name_scheme["fields"]: + row.append( + get_name_parts_localized( + order.invoice_address.name_parts, k + ) + ) + row += [ + order.invoice_address.street, + order.invoice_address.zipcode, + order.invoice_address.city, + ( + order.invoice_address.country + if order.invoice_address.country + else order.invoice_address.country_old + ), + order.invoice_address.state, + order.invoice_address.vat_id, + ] + except InvoiceAddress.DoesNotExist: + row += [""] * ( + 8 + + ( + len(name_scheme["fields"]) + if name_scheme and len(name_scheme["fields"]) > 1 + else 0 + ) + ) + row.append( + str(order.customer.external_identifier) + if order.customer and order.customer.external_identifier + else "" + ) + row.append( + ", ".join( + [ + str(self.providers.get(p, p)) + for p in sorted(set((op.payment_providers or "").split(","))) + if p and p != "free" + ] + ) + ) + row += self.event_object_cache[order.event_id].meta_data.values() + yield row + + def positions_qs(self, form_data: dict): + qs = OrderPosition.all.filter( + order__event__in=self.events, + ) + if form_data["paid_only"]: + qs = qs.filter(order__status=Order.STATUS_PAID, canceled=False) + + qs = self._date_filter(qs, form_data, rel="order__") + return qs + + # Bestellzeilen: Persönliche Daten, sehr viel Umsortieren ggü orderlist.py, deshalb TODO + def iterate_positions(self, form_data: dict): + base_qs = self.positions_qs(form_data) + + p_providers = ( + OrderPayment.objects.filter( + order=OuterRef("order"), + state__in=( + OrderPayment.PAYMENT_STATE_CONFIRMED, + OrderPayment.PAYMENT_STATE_REFUNDED, + OrderPayment.PAYMENT_STATE_PENDING, + OrderPayment.PAYMENT_STATE_CREATED, + ), + ) + .values("order") + .annotate(m=GroupConcat("provider", delimiter=",")) + .values("m") + .order_by() + ) + qs = ( + base_qs.annotate( + payment_providers=Subquery(p_providers, output_field=CharField()), + checked_in_lists=Subquery( + Checkin.objects.filter( + successful=True, + type=Checkin.TYPE_ENTRY, + position=OuterRef("pk"), + ) + .order_by() + .values("position") + .annotate( + c=GroupConcat( + "list__name", + # These appear not to work properly on SQLite. Well, we don't support SQLite outside testing + # anyways. + ordered="sqlite" + not in settings.DATABASES["default"]["ENGINE"], + distinct="sqlite" + not in settings.DATABASES["default"]["ENGINE"], + delimiter=", ", + ) + ) + .values("c") + ), + ) + .select_related( + "order", + "order__invoice_address", + "order__customer", + "item", + "variation", + "voucher", + "tax_rule", + "addon_to", + ) + .prefetch_related( + "subevent", + "subevent__meta_values", + "answers", + "answers__question", + "answers__options", + ) + ) + + has_subevents = self.events.filter(has_subevents=True).exists() + + # HEADERS START + # WICHTIG: + headers = [ + _("Product"), # wichtig + ] + questions = list(Question.objects.filter(event__in=self.events)) # für wichtig + options = defaultdict(list) # für wichtig + for q in questions: # wichtig + if q.type == Question.TYPE_CHOICE_MULTIPLE: + if form_data["group_multiple_choice"]: + for o in q.options.all(): + options[q.pk].append(o) + headers.append(str(q.question)) + else: + for o in q.options.all(): + headers.append(str(q.question) + " – " + str(o.answer)) + options[q.pk].append(o) + else: + if q.type == Question.TYPE_CHOICE: + for o in q.options.all(): + options[q.pk].append(o) + headers.append(str(q.question)) + + # MITTELWICHTIG: + headers += [ + _("Status"), # mittelwichtig + _("Email"), # mittelwichtig + ] + + # UNWICHTIG: + headers += [ + _("Order code"), # unwichtig + _("Order date"), # unwichtig + _("Order time"), # unwichtig + ] + if has_subevents: # unwichtig, unser Event hat das aber auch nicht + headers.append(pgettext("subevent", "Date")) + headers.append(_("Start date")) + headers.append(_("End date")) + headers += [ + _("Product ID"), # unwichtig + _("Attendee name"), # unwichtig + ] + name_scheme = ( # für unwichtig: Namensschema + PERSON_NAME_SCHEMES[self.event.settings.name_scheme] + if not self.is_multievent + else None + ) + if name_scheme and len(name_scheme["fields"]) > 1: # unwichtig: Namensschema + for k, label, w in name_scheme["fields"]: + headers.append(_("Attendee name") + ": " + str(label)) + headers += [ + _("Attendee email"), # unwichtig + _("Order comment"), # unwichtig + _("Payment providers"), # unwichtig + ] + + # get meta_data labels from first cached event + meta_data_labels = next(iter(self.event_object_cache.values())).meta_data.keys() + if has_subevents: # unwichtig + headers += meta_data_labels + + # SEHR UNWICHTIG: + headers += [ + _("Event slug"), # sehr unwichtig + _("Event name"), # sehr unwichtig + _("Position ID"), # sehr unwichtig + _("Phone number"), # sehr unwichtig + _("Variation"), # sehr unwichtig + _("Variation ID"), # sehr unwichtig + _("Price"), # sehr unwichtig + _("Tax rate"), # sehr unwichtig + _("Tax rule"), # sehr unwichtig + _("Tax value"), # sehr unwichtig + _("Company"), # sehr unwichtig + _("Address"), # sehr unwichtig + _("ZIP code"), # sehr unwichtig + _("City"), # sehr unwichtig + _("Country"), # sehr unwichtig + pgettext("address", "State"), # sehr unwichtig + _("Voucher"), # sehr unwichtig + _("Pseudonymization ID"), # sehr unwichtig + _("Ticket secret"), # sehr unwichtig + _("Seat ID"), # sehr unwichtig + _("Seat name"), # sehr unwichtig + _("Seat zone"), # sehr unwichtig + _("Seat row"), # sehr unwichtig + _("Seat number"), # sehr unwichtig + _("Blocked"), # sehr unwichtig + _("Valid from"), # sehr unwichtig + _("Valid until"), # sehr unwichtig + _("Follow-up date"), # sehr unwichtig + _("Add-on to position ID"), # sehr unwichtig + _("Company"), # sehr unwichtig + _("Invoice address name"), # sehr unwichtig + ] + if name_scheme and len(name_scheme["fields"]) > 1: # sehr unwichtig + for k, label, w in name_scheme["fields"]: + headers.append(_("Invoice address name") + ": " + str(label)) + headers += [ + _("Invoice address street"), # sehr unwichtig + _("Invoice address ZIP code"), # sehr unwichtig + _("Invoice address city"), # sehr unwichtig + _("Invoice address country"), # sehr unwichtig + pgettext("address", "Invoice address state"), # sehr unwichtig + _("VAT ID"), # sehr unwichtig + _("Sales channel"), # sehr unwichtig + _("Order locale"), # sehr unwichtig + _("Email address verified"), # sehr unwichtig + _("External customer ID"), # sehr unwichtig + _("Check-in lists"), # sehr unwichtig + ] + + # UNSORTIERT: + yield headers + # HEADERS END + + all_ids = list( + base_qs.order_by("order__datetime", "positionid").values_list( + "pk", flat=True + ) + ) + yield self.ProgressSetTotal(total=len(all_ids)) + for ids in chunked_iterable(all_ids, 1000): + ops = sorted(qs.filter(id__in=ids), key=lambda k: ids.index(k.pk)) + + for op in ops: + order = op.order + tz = ZoneInfo(self.event_object_cache[order.event_id].settings.timezone) + # ROWS START + # WICHTIG: + row = [ + str(op.item), # wichtig_("Product") + ] + # Eigene Fragen Start + acache = {} # für wichtig (Eigene Fragen) + for a in op.answers.all(): # für wichtig (Eigene Fragen) + # We do not want to localize Date, Time and Datetime question answers, as those can lead + # to difficulties parsing the data (for example 2019-02-01 may become Février, 2019 01 in French). + if a.question.type in ( + Question.TYPE_CHOICE_MULTIPLE, + Question.TYPE_CHOICE, + ): + acache[a.question_id] = set(o.pk for o in a.options.all()) + elif a.question.type in Question.UNLOCALIZED_TYPES: + acache[a.question_id] = a.answer + else: + acache[a.question_id] = str(a) + for q in questions: # wichtig (Eigene Fragen) + if q.type == Question.TYPE_CHOICE_MULTIPLE: + if form_data["group_multiple_choice"]: + row.append( + ", ".join( + str(o.answer) + for o in options[q.pk] + if o.pk in acache.get(q.pk, set()) + ) + ) + else: + for o in options[q.pk]: + row.append( + _("Yes") + if o.pk in acache.get(q.pk, set()) + else _("No") + ) + elif q.type == Question.TYPE_CHOICE: + # Join is only necessary if the question type was modified but also keeps the code simpler here + # as we'd otherwise need some [0] and existance checks + row.append( + ", ".join( + str(o.answer) + for o in options[q.pk] + if o.pk in acache.get(q.pk, set()) + ) + ) + else: + row.append(acache.get(q.pk, "")) + # Eigene Fragen End + + # MITTELWICHTIG: + row += [ + ( # mittelwichtig _("Status") + _("canceled") + if op.canceled + else order.get_extended_status_display() + ), + order.email, # mittelwichtig_("Email" + ] + + # UNWICHTIG: + row += [ + order.code, # unwichtig_("Order code") + order.datetime.astimezone(tz).strftime( + "%Y-%m-%d" + ), # unwichtig _("Order date") + order.datetime.astimezone(tz).strftime( + "%H:%M:%S" + ), # unwichtig _("Order time") + ] + if has_subevents: # unwichtig , unser Event hat das aber auch nicht + if op.subevent: + row.append(op.subevent.name) + row.append( + op.subevent.date_from.astimezone( + self.event_object_cache[order.event_id].timezone + ).strftime("%Y-%m-%d %H:%M:%S") + ) + if op.subevent.date_to: + row.append( + op.subevent.date_to.astimezone( + self.event_object_cache[order.event_id].timezone + ).strftime("%Y-%m-%d %H:%M:%S") + ) + else: + row.append("") + else: + row.append("") + row.append("") + row.append("") + row += [ + str(op.item_id), # unwichtig _("Product ID") + op.attendee_name, # unwichtig _("Attendee name") + ] + if name_scheme and len(name_scheme["fields"]) > 1: # für unwichtig + for k, label, w in name_scheme["fields"]: + row.append(get_name_parts_localized(op.attendee_name_parts, k)) + row += [ + op.attendee_email, # unwichtig _("Attendee email") + ] + row.append(order.comment) # unwichtig_("Order comment") + row.append( # unwichtig_("Payment providers") + ", ".join( + [ + str(self.providers.get(p, p)) + for p in sorted( + set((op.payment_providers or "").split(",")) + ) + if p and p != "free" + ] + ) + ) + + if has_subevents: # unwichtig + if op.subevent: + row += op.subevent.meta_data.values() + else: + row += [""] * len(meta_data_labels) + + # SEHR UNWICHTIG: + row += [ + self.event_object_cache[ # sehr unwichtig_("Event slug") + order.event_id + ].slug, + str( # sehr unwichtig_("Event name") + self.event_object_cache[order.event_id].name + ), + op.positionid, # sehr unwichtig_("Position ID") + ( + str(order.phone) if order.phone else "" + ), # sehr unwichtig_("Phone number") + ( + str(op.variation) if op.variation else "" + ), # sehr unwichtig_("Variation") + ( + str(op.variation_id) if op.variation_id else "" + ), # sehr unwichtig_("Variation ID") + op.price, # sehr unwichtig_("Price") + op.tax_rate, # sehr unwichtig_("Tax rate") + ( + str(op.tax_rule) if op.tax_rule else "" + ), # sehr unwichtig_("Tax rule") + op.tax_value, # sehr unwichtig_("Tax value") + op.company or "", # sehr unwichtig_("Company") + op.street or "", # sehr unwichtig_("Address") + op.zipcode or "", # sehr unwichtig_("ZIP code") + op.city or "", # sehr unwichtig_("City") + op.country if op.country else "", # sehr unwichtig_("Country") + op.state or "", # sehr unwichtig("address", "State") + op.voucher.code if op.voucher else "", # sehr unwichtig_("Voucher") + op.pseudonymization_id, # sehr unwichtig_("Pseudonymization ID") + op.secret, # sehr unwichtig_("Ticket secret") + ] + if op.seat: # sehr unwichtig + row += [ + op.seat.seat_guid, # sehr unwichtig_("Seat ID") + str(op.seat), # sehr unwichtig_("Seat name") + op.seat.zone_name, # sehr unwichtig_("Seat zone") + op.seat.row_name, # sehr unwichtig_("Seat row") + op.seat.seat_number, # sehr unwichtig_("Seat number") + ] + else: + row += ["", "", "", "", ""] # sehr unwichtig + row += [ # sehr unwichtig + _("Yes") if op.blocked else "", # sehr unwichtig_("Blocked") + ( # sehr unwichtig_("Valid from" + date_format( + op.valid_from.astimezone(tz), "SHORT_DATETIME_FORMAT" + ) + if op.valid_from + else "" + ), + ( # sehr unwichtig_("Valid until") + date_format( + op.valid_until.astimezone(tz), "SHORT_DATETIME_FORMAT" + ) + if op.valid_until + else "" + ), + ] + row.append( # sehr unwichtig("Follow-up date") + order.custom_followup_at.strftime("%Y-%m-%d") + if order.custom_followup_at + else "" + ) + row.append( + op.addon_to.positionid if op.addon_to_id else "" + ) # sehr unwichtig_("Add-on to position ID") + try: # sehr unwichtig + row += [ + order.invoice_address.company, # sehr unwichtig_("Company") + order.invoice_address.name, # sehr unwichtig_("Invoice address name") + ] + if ( + name_scheme and len(name_scheme["fields"]) > 1 + ): # sehr unwichtig_("Invoice address name: {fields}") + for k, label, w in name_scheme["fields"]: + row.append( + get_name_parts_localized( + order.invoice_address.name_parts, k + ) + ) + row += [ + order.invoice_address.street, # sehr unwichtig_("Invoice address street") + order.invoice_address.zipcode, # sehr unwichtig_("Invoice address ZIP code") + order.invoice_address.city, # sehr unwichtig_("Invoice address city") + ( # sehr unwichtig_("Invoice address country") + order.invoice_address.country + if order.invoice_address.country + else order.invoice_address.country_old + ), + order.invoice_address.state, # sehr unwichtig("address", "Invoice address state") + order.invoice_address.vat_id, # sehr unwichtig_("VAT ID") + ] + except InvoiceAddress.DoesNotExist: + row += [""] * ( + 8 + + ( + len(name_scheme["fields"]) + if name_scheme and len(name_scheme["fields"]) > 1 + else 0 + ) + ) + row += [ # sehr unwichtig + order.sales_channel, # sehr unwichtig_("Sales channel") + order.locale, # sehr unwichtig_("Order locale") + ( + _("Yes") if order.email_known_to_work else _("No") + ), # sehr unwichtig_("Email address verified") + ( # sehr unwichtig_("External customer ID") + str(order.customer.external_identifier) + if order.customer and order.customer.external_identifier + else "" + ), + ] + row.append( + op.checked_in_lists or "" + ) # sehr unwichtig_("Check-in lists") + + # UNSORTIERT: + + yield row + # ROWS END + + def get_filename(self): + if self.is_multievent: + return "{}_orders".format(self.organizer.slug) + else: + return "{}_orders".format(self.event.slug) + + +@receiver(register_data_exporters, dispatch_uid="exporter_relevant_orderlist") +def register_relevant_orderlist_exporter(sender, **kwargs): + return RelevantOrderListExporter + + +@receiver( + register_multievent_data_exporters, dispatch_uid="multiexporter_relevant_orderlist" +) +def register_multievent_relevant_orderlist_exporter(sender, **kwargs): + return RelevantOrderListExporter diff --git a/src/pretix/locale/de/LC_MESSAGES/django.po b/src/pretix/locale/de/LC_MESSAGES/django.po index 40d465adb..ee5491938 100644 --- a/src/pretix/locale/de/LC_MESSAGES/django.po +++ b/src/pretix/locale/de/LC_MESSAGES/django.po @@ -840,7 +840,7 @@ msgstr "Details der Bestellposition" #: pretix/base/datasync/sourcefields.py:129 msgid "Attendee details" -msgstr "Details Teilnehmer*in" +msgstr "Details Teilnehmer" #: pretix/base/datasync/sourcefields.py:130 pretix/base/exporters/answers.py:66 #: pretix/base/models/items.py:1766 pretix/control/navigation.py:172 @@ -902,13 +902,13 @@ msgstr "Veranstaltungs- oder Termininformationen" #: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:50 #: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html:36 msgid "Attendee name" -msgstr "Name Teilnehmer*in" +msgstr "Name Teilnehmer" #: pretix/base/datasync/sourcefields.py:187 #: pretix/base/datasync/sourcefields.py:604 #: pretix/base/datasync/sourcefields.py:628 msgid "Attendee" -msgstr "Teilnehmer*in" +msgstr "Teilnehmer" #: pretix/base/datasync/sourcefields.py:207 #: pretix/base/exporters/orderlist.py:612 pretix/base/forms/questions.py:687 @@ -919,11 +919,11 @@ msgstr "Teilnehmer*in" #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:172 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:175 msgid "Attendee email" -msgstr "E-Mail Teilnehmer*in" +msgstr "E-Mail Teilnehmer" #: pretix/base/datasync/sourcefields.py:219 msgid "Attendee or order email" -msgstr "E-Mail Teilnehmer*in oder Bestellung" +msgstr "E-Mail Teilnehmer oder Bestellung" #: pretix/base/datasync/sourcefields.py:232 #: pretix/base/exporters/orderlist.py:613 pretix/base/pdf.py:189 @@ -933,23 +933,23 @@ msgstr "E-Mail Teilnehmer*in oder Bestellung" #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:182 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:185 msgid "Attendee company" -msgstr "Teilnehmer*innen-Firma" +msgstr "Teilnehmer-Firma" #: pretix/base/datasync/sourcefields.py:241 msgid "Attendee address street" -msgstr "Teilnehmer*innen-Adresse: Straße" +msgstr "Teilnehmer-Adresse: Straße" #: pretix/base/datasync/sourcefields.py:250 msgid "Attendee address ZIP code" -msgstr "Teilnehmer*innen-Adresse: PLZ" +msgstr "Teilnehmer-Adresse: PLZ" #: pretix/base/datasync/sourcefields.py:259 msgid "Attendee address city" -msgstr "Teilnehmer*innen-Adresse: Stadt" +msgstr "Teilnehmer-Adresse: Stadt" #: pretix/base/datasync/sourcefields.py:268 msgid "Attendee address country" -msgstr "Teilnehmer*innen-Adresse: Land" +msgstr "Teilnehmer-Adresse: Land" #: pretix/base/datasync/sourcefields.py:279 #: pretix/base/exporters/orderlist.py:653 pretix/base/pdf.py:347 @@ -1226,7 +1226,7 @@ msgid "" "Download a ZIP file including all files that have been uploaded by your " "customers while creating an order." msgstr "" -"ZIP-Datei mit allen Dateien, die von Kund*innen im Bestellprozess als " +"ZIP-Datei mit allen Dateien, die von Kunden im Bestellprozess als " "Antwort auf eine Frage hochgeladen wurden." #: pretix/base/exporters/answers.py:76 pretix/base/exporters/orderlist.py:593 @@ -2178,6 +2178,10 @@ msgstr "Ohne gültige Mitgliedschaft verstecken" msgid "Order data" msgstr "Bestelldaten" +#: pretix/base/exporters/relevant_orderlist.py:86 +msgid "Order data (sorted by relevance)" +msgstr "Bestelldaten (nach Relevanz sortiert)" + #: pretix/base/exporters/json.py:53 msgid "" "Download a structured JSON representation of all orders. This might be " @@ -2195,8 +2199,8 @@ msgid "" "Download a text file with all email addresses collected either from buyers " "or from ticket holders." msgstr "" -"Textdatei mit allen E-Mail-Adressen, die von Käufer*innen und " -"Ticketinhaber*innen eingesammelt wurden." +"Textdatei mit allen E-Mail-Adressen, die von Käufern und " +"Ticketinhaber eingesammelt wurden." #: pretix/base/exporters/mail.py:76 pretix/plugins/reports/exporters.py:498 #: pretix/plugins/reports/exporters.py:681 @@ -2214,6 +2218,18 @@ msgstr "" "Bestellposition und das dritte eine Zeile für jede zusätzlich erhobene " "Gebühr." +#: pretix/base/exporters/relevant_orderlist.py:88 +msgid "" +"Download a spreadsheet of all orders. The spreadsheet will include three " +"sheets, one with a line for every order, one with a line for every order " +"position, and one with a line for every additional fee charged in an " +"order. The most relevant data is in the first columns of the tables." +msgstr "" +"Tabelle (Excel oder CSV) mit allen Bestellungen. Das erste Tabellenblatt " +"enthält eine Zeile für jede Bestellung, das zweite eine Zeile für jede " +"Bestellposition und das dritte eine Zeile für jede zusätzlich erhobene " +"Gebühr. Die relevantesten Daten sind in den ersten Spalten der Tabellen." + #: pretix/base/exporters/orderlist.py:102 pretix/base/models/orders.py:337 #: pretix/control/navigation.py:255 pretix/control/navigation.py:362 #: pretix/control/templates/pretixcontrol/orders/index.html:8 @@ -4086,7 +4102,7 @@ msgstr "Bitte wählen Sie einen gültigen Staat aus." #: pretix/base/modelimport_orders.py:359 pretix/control/forms/filter.py:683 msgid "Attendee email address" -msgstr "Teilnehmer*innen-E-Mail-Adresse" +msgstr "Teilnehmer-E-Mail-Adresse" #: pretix/base/modelimport_orders.py:375 pretix/base/modelimport_orders.py:386 #: pretix/base/modelimport_orders.py:397 pretix/base/modelimport_orders.py:408 @@ -4098,7 +4114,7 @@ msgstr "Teilnehmer*innen-E-Mail-Adresse" #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:193 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:196 msgid "Attendee address" -msgstr "Teilnehmer*innen-Adresse" +msgstr "Teilnehmer-Adresse" #: pretix/base/modelimport_orders.py:445 msgid "State" @@ -7978,7 +7994,7 @@ msgstr "Musterfirma GmbH" #: pretix/base/pdf.py:194 msgid "Full attendee address" -msgstr "Volle Teilnehmer*innen-Adresse" +msgstr "Volle Teilnehmer-Adresse" #: pretix/base/pdf.py:195 msgid "" @@ -7996,23 +8012,23 @@ msgstr "" #: pretix/base/pdf.py:199 msgid "Attendee street" -msgstr "Teilnehmer*innen-Straße" +msgstr "Teilnehmer-Straße" #: pretix/base/pdf.py:204 msgid "Attendee ZIP code" -msgstr "Teilnehmer*innen-PLZ" +msgstr "Teilnehmer-PLZ" #: pretix/base/pdf.py:209 msgid "Attendee city" -msgstr "Teilnehmer*innen-Stadt" +msgstr "Teilnehmer-Stadt" #: pretix/base/pdf.py:214 msgid "Attendee state" -msgstr "Teilnehmer*innen-Bundesstaat" +msgstr "Teilnehmer-Bundesstaat" #: pretix/base/pdf.py:219 msgid "Attendee country" -msgstr "Teilnehmer*innen-Land" +msgstr "Teilnehmer-Land" #: pretix/base/pdf.py:231 msgid "Pseudonymization ID (lead scanning)" @@ -8305,7 +8321,7 @@ msgstr "Herr Mustermann" #: pretix/plugins/ticketoutputpdf/exporters.py:99 #, python-brace-format msgid "Attendee name: {part}" -msgstr "Teilnehmer*innenname: {part}" +msgstr "Teilnehmername: {part}" #: pretix/base/pdf.py:689 msgid "Invoice address name for salutation" @@ -9003,7 +9019,7 @@ msgstr "Veranstaltungsort: {location}" #, python-brace-format msgctxt "invoice" msgid "Attendee: {name}" -msgstr "Teilnehmer*in: {name}" +msgstr "Teilnehmer: {name}" #: pretix/base/services/invoices.py:278 pretix/plugins/reports/exporters.py:309 #, python-brace-format @@ -9833,7 +9849,7 @@ msgstr "" #: pretix/base/settings.py:347 msgid "Hide prices on attendee ticket page" -msgstr "Preise auf Teilnehmer*innen-Ticket-Seite verstecken" +msgstr "Preise auf Teilnehmer-Ticket-Seite verstecken" #: pretix/base/settings.py:348 msgid "" @@ -9843,13 +9859,13 @@ msgid "" "price." msgstr "" "Wenn eine Person mehrere Tickets erwirbt und E-Mails an alle " -"Teilnehmer*innen verschickt werden, wird mit dieser Option der Ticketpreis " -"auf der Ticket-Seite der einzelnen Teilnehmer*innen versteckt. Nur die " +"Teilnehmer verschickt werden, wird mit dieser Option der Ticketpreis " +"auf der Ticket-Seite der einzelnen Teilnehmer versteckt. Nur die " "Person, welche die Tickets kauft, sieht den Preis." #: pretix/base/settings.py:366 msgid "Ask for attendee names" -msgstr "Namen der Teilnehmer*innen erfragen" +msgstr "Namen der Teilnehmer erfragen" #: pretix/base/settings.py:367 msgid "Ask for a name for all personalized tickets." @@ -9858,11 +9874,11 @@ msgstr "" #: pretix/base/settings.py:376 msgid "Require attendee names" -msgstr "Namen der Teilnehmer*innen erfordern" +msgstr "Namen der Teilnehmer erfordern" #: pretix/base/settings.py:377 msgid "Require customers to fill in the names of all attendees." -msgstr "Erfordere die Eingabe aller Teilnehmer*innen-Namen." +msgstr "Erfordere die Eingabe aller Teilnehmer-Namen." #: pretix/base/settings.py:387 msgid "Ask for email addresses per ticket" @@ -10373,8 +10389,8 @@ msgid "" "but no indication of missing payment will be visible on the ticket pages of " "attendees who did not buy the ticket themselves." msgstr "" -"Die Box mit Zahlungsinstruktionen wird Ticketkäufer*innen weiter angezeigt, " -"aber Teilnehmer*innen, die ihr Ticket nicht selbst gekauft haben, werden " +"Die Box mit Zahlungsinstruktionen wird Ticketkäufern weiter angezeigt, " +"aber Teilnehmern, die ihr Ticket nicht selbst gekauft haben, werden " "keine Anzeichen des fehlenden Zahlungseingangs sehen." #: pretix/base/settings.py:1027 @@ -10868,7 +10884,7 @@ msgstr "" #: pretix/base/settings.py:1638 msgid "Show number of check-ins to customer" -msgstr "Zeige Anzahl der Check-ins für Kund*innen an" +msgstr "Zeige Anzahl der Check-ins für Kunden an" #: pretix/base/settings.py:1639 msgid "" @@ -10879,12 +10895,12 @@ msgid "" "failed scans will not be counted, and the user will not see the different " "check-in lists." msgstr "" -"Wenn diese Option aktiv ist, können Kund*innen selbst sehen, wie oft sie die " +"Wenn diese Option aktiv ist, können Kunden selbst sehen, wie oft sie die " "Veranstaltung betreten haben. Das ist normalerweise nicht nötig, aber kann " "nützlich sein, wenn es Tickets gibt, die eine bestimmte Anzahl an Eintritten " -"erlauben, sodass Kund*innen die bisherige Nutzung des Tickets einsehen " +"erlauben, sodass Kunden die bisherige Nutzung des Tickets einsehen " "können. Ausgänge oder fehlgeschlagene Scans werden nicht angezeigt und die " -"Kund*innen sehen keine Aufschlüsselung verschiedener Check-in-Listen." +"Kunden sehen keine Aufschlüsselung verschiedener Check-in-Listen." #: pretix/base/settings.py:1652 msgid "Allow users to download tickets" @@ -11065,7 +11081,7 @@ msgstr "" #: pretix/base/settings.py:1815 pretix/base/settings.py:1824 msgid "Both the attendee and the person who ordered can make changes" msgstr "" -"Sowohl Besteller*in als auch Teilnehmer*innen können Änderungen vornehmen" +"Sowohl Besteller als auch Teilnehmer können Änderungen vornehmen" #: pretix/base/settings.py:1819 msgid "Allow customers to modify their information" @@ -11173,7 +11189,7 @@ msgstr "" #: pretix/base/settings.py:1925 msgid "Allow individual attendees to change their ticket" -msgstr "Erlaubt einzelnen Teilnehmer*innen ihr Ticket zu ändern" +msgstr "Erlaubt einzelnen Teilnehmern ihr Ticket zu ändern" #: pretix/base/settings.py:1926 msgid "" @@ -11185,7 +11201,7 @@ msgid "" msgstr "" "Standardmäßig kann nur die Person, welche die Tickets gekauft hat, " "Änderungen an der Bestellung vornehmen. Wenn diese Option aktiv ist, können " -"auch einzelne Teilnehmer*innen Änderungen vornehmen. Teilnehmer*innen können " +"auch einzelne Teilnehmer Änderungen vornehmen. Teilnehmer können " "jedoch immer nur Änderungen vornehmen, welche die Gesamtkosten der " "Bestellung nicht verändern. Solche Änderungen können nur vom Ticketkäufer " "vorgenommen werden." @@ -11481,7 +11497,7 @@ msgid "" "people." msgstr "" "Sie können dieses Feld benutzen, um zusätzliche Informationen mit Ihren " -"Teilnehmer*innen zu teilen, wie z.B. Anreise-Informationen oder den Link zu " +"Teilnehmer zu teilen, wie z.B. Anreise-Informationen oder den Link zu " "einer digitalen Veranstaltung. Wenn das Feld leer ist, fügen wir automatisch " "einen Link zum Ticketshop, die Einlass-Uhrzeit und den Veranstalter hier " "ein. Es sind keine Platzhalter mit sensiblen personenbezogenen Daten " @@ -11722,8 +11738,8 @@ msgstr "" "Diese Datei wird an die erste E-Mail angehängt, die wir beim Eingang einer " "neuen Bestellung verschicken. Sie kann daher mit den Textvorlagen " "\"Getätigte Bestellung\", \"Kostenlose Bestellung\" oder \"Erhaltene " -"Bestellung\" von oben auftreten. Sie wird ggf. sowohl an Besteller*innen als " -"auch Teilnehmer*innen verschickt. Nicht geeignet zum Versand nicht-" +"Bestellung\" von oben auftreten. Sie wird ggf. sowohl an Besteller als " +"auch Teilnehmer verschickt. Nicht geeignet zum Versand nicht-" "öffentlicher Informationen, da die Datei unabhängig davon verschickt wird, " "ob die Bestellung bezahlt oder freigegeben ist. Um zu vermeiden, dass diese " "wichtige E-Mail nicht ankommt, können nur PDF-Dateien mit maximal {size} MB " @@ -14927,7 +14943,7 @@ msgstr "Check-in-Status" #: pretix/control/forms/filter.py:1913 #: pretix/plugins/checkinlists/exporters.py:108 msgid "All attendees" -msgstr "Alle Teilnehmer*innen" +msgstr "Alle Teilnehmer" #: pretix/control/forms/filter.py:1914 #: pretix/control/templates/pretixcontrol/checkin/index.html:183 @@ -15507,7 +15523,7 @@ msgid "" "people over 65. This ticket includes access to all parts of the event, " "except the VIP area." msgstr "" -"z.B. Dieses reduzierte Ticket ist erhältlich für Vollzeitstudent*innen, " +"z.B. Dieses reduzierte Ticket ist erhältlich für Vollzeitstudent, " "Arbeitslose und Menschen über 65. Das Ticket enthält Zugang zu allen Teilen " "der Veranstaltung außer des VIP-Bereiches." @@ -15821,7 +15837,7 @@ msgstr "" #: pretix/control/forms/orders.py:167 pretix/control/forms/orders.py:226 #: pretix/control/forms/orders.py:240 msgid "Notify customer by email" -msgstr "Kund*in per E-Mail benachrichtigen" +msgstr "Kunde per E-Mail benachrichtigen" #: pretix/control/forms/orders.py:174 msgid "Keep a cancellation fee of" @@ -17312,7 +17328,7 @@ msgstr "Eine individuelle E-Mail wurde verschickt." #: pretix/control/logdisplay.py:547 msgid "A custom email has been sent to an attendee." -msgstr "Eine individuelle E-Mail wurde an eine Teilnehmer*in verschickt." +msgstr "Eine individuelle E-Mail wurde an einen Teilnehmer verschickt." #: pretix/control/logdisplay.py:548 msgid "" @@ -19021,7 +19037,7 @@ msgstr "Terminal-ID" #: pretix/control/templates/pretixcontrol/boxoffice/payment.html:104 msgid "Card holder" -msgstr "Karteninhaber*in" +msgstr "Karteninhaber" #: pretix/control/templates/pretixcontrol/boxoffice/payment.html:108 msgid "Card expiration" @@ -19324,7 +19340,7 @@ msgstr "CSV" #: pretix/control/templates/pretixcontrol/checkin/index.html:73 msgid "No attendee record was found." -msgstr "Keine passenden Teilnehmer*innen gefunden." +msgstr "Keine passenden Teilnehmer gefunden." #: pretix/control/templates/pretixcontrol/checkin/index.html:91 #: pretix/control/templates/pretixcontrol/datasync/failed_jobs.html:19 @@ -22163,7 +22179,7 @@ msgid "" "Only purchases of such products will be considered \"attendees\" for most " "statistical purposes or within some plugins." msgstr "" -"Nur Käufe eines solchen Produkts werden als \"Teilnehmer*innen\" gewertet, " +"Nur Käufe eines solchen Produkts werden als \"Teilnehmer\" gewertet, " "z.B. in Statistiken oder in Funktionen von Erweiterungen." #: pretix/control/templates/pretixcontrol/item/create.html:39 @@ -22242,7 +22258,7 @@ msgid "" "The system will not ask for a name or other attendee details. This only " "affects system-provided fields, you can still add your own questions." msgstr "" -"Das System wird nicht nach einem Namen oder anderen Teilnehmer*innen-Daten " +"Das System wird nicht nach einem Namen oder anderen Teilnehmer-Daten " "fragen. Dies betrifft nur vom System bereitgestellte Felder, eigene Fragen " "können trotzdem hinzugefügt werden." @@ -24515,7 +24531,7 @@ msgstr "Sonstige Datenexporte" #: pretix/control/templates/pretixcontrol/orders/export.html:104 #: pretix/control/templates/pretixcontrol/organizers/export.html:104 msgid "Recommended for new users" -msgstr "Empfohlen für neue Benutzer*innen" +msgstr "Empfohlen für neue Benutzer" #: pretix/control/templates/pretixcontrol/orders/export_delete.html:4 #: pretix/control/templates/pretixcontrol/orders/export_delete.html:6 @@ -25482,7 +25498,7 @@ msgstr "Domains" #: pretix/control/templates/pretixcontrol/organizers/edit.html:319 msgid "This dialog is intended for advanced users." -msgstr "Dieser Dialog ist für fortgeschrittene Anwender*innen gedacht." +msgstr "Dieser Dialog ist für fortgeschrittene Anwender gedacht." #: pretix/control/templates/pretixcontrol/organizers/edit.html:320 msgid "" @@ -27765,7 +27781,7 @@ msgid "" "customers. This way, customers will not be able to discover the waiting list." msgstr "" "Entsprechend Ihrer Veranstaltungseinstellungen werden ausverkaufte Produkte " -"nicht angezeigt. Dies führt dazu, dass Kund*innen die Warteliste nicht " +"nicht angezeigt. Dies führt dazu, dass Kunden die Warteliste nicht " "finden können." #: pretix/control/templates/pretixcontrol/waitinglist/index.html:36 @@ -28093,11 +28109,11 @@ msgstr "Das ausgewählte List wurde gelöscht." #: pretix/control/views/dashboards.py:115 msgid "Attendees (ordered)" -msgstr "Teilnehmende (bestellt)" +msgstr "Teilnehmer (bestellt)" #: pretix/control/views/dashboards.py:125 msgid "Attendees (paid)" -msgstr "Teilnehmende (bezahlt)" +msgstr "Teilnehmer (bezahlt)" #: pretix/control/views/dashboards.py:137 #, python-brace-format @@ -30136,7 +30152,7 @@ msgstr "PDF-Sammlungen" #: pretix/plugins/badges/exporters.py:423 msgid "Download all attendee badges as one large PDF for printing." -msgstr "Alle Teilnehmer*innen-Badges in einer großen PDF-Datei für den Druck." +msgstr "Alle Teilnehmer-Badges in einer großen PDF-Datei für den Druck." #: pretix/plugins/badges/exporters.py:444 #: pretix/plugins/ticketoutputpdf/exporters.py:80 @@ -30407,7 +30423,7 @@ msgstr "Anderes Bankkonto" #: pretix/plugins/banktransfer/payment.py:85 msgid "Name of account holder" -msgstr "Kontoinhaber*in" +msgstr "Kontoinhaber" #: pretix/plugins/banktransfer/payment.py:87 msgid "" @@ -30549,7 +30565,7 @@ msgstr "Bitte überweisen Sie den vollen Betrag auf das folgende Bankkonto:" #: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:32 #: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:35 msgid "Account holder" -msgstr "Kontoinhaber*in" +msgstr "Kontoinhaber" #: pretix/plugins/banktransfer/payment.py:303 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/checkout_payment_form.html:20 @@ -31159,7 +31175,7 @@ msgid "" "Download a spreadsheet with all attendees that are included in a check-in " "list." msgstr "" -"Tabelle (Excel oder CSV) mit allen Teilnehmer*innen, die in einer Check-in-" +"Tabelle (Excel oder CSV) mit allen Teilnehmer, die in einer Check-in-" "Liste zutrittsberechtigt sind." #: pretix/plugins/checkinlists/exporters.py:501 @@ -32339,7 +32355,7 @@ msgstr "Geplante E-Mails" #: pretix/plugins/sendmail/signals.py:122 msgid "Mass email was sent to customers or attendees." -msgstr "Rundmail wurde an Kunden oder Teilnehmer*innen verschickt." +msgstr "Rundmail wurde an Kunden oder Teilnehmer verschickt." #: pretix/plugins/sendmail/signals.py:123 msgid "Mass email was sent to waiting list entries." @@ -32371,7 +32387,7 @@ msgstr "Eine automatisierte E-Mail wurde an den Besteller verschickt" #: pretix/plugins/sendmail/signals.py:142 msgid "A scheduled email was sent to a ticket holder" -msgstr "Eine automatisierte E-Mail wurde an eine Teilnehmer*in verschickt." +msgstr "Eine automatisierte E-Mail wurde an eine Teilnehmer verschickt." #: pretix/plugins/sendmail/signals.py:143 msgid "An email rule was deleted" @@ -32404,7 +32420,7 @@ msgstr "Alle nicht eingecheckten Kunden" #: pretix/plugins/sendmail/templates/pretixplugins/sendmail/history_fragment_orders.html:23 msgid "Attendee contact addresses" -msgstr "Teilnehmer*innen-E-Mail-Adressen" +msgstr "Teilnehmer-E-Mail-Adressen" #: pretix/plugins/sendmail/templates/pretixplugins/sendmail/history_fragment_orders.html:25 msgid "All contact addresses" @@ -32563,14 +32579,14 @@ msgstr "" #: pretix/plugins/sendmail/views.py:250 msgid "Orders or attendees" -msgstr "Bestellungen oder Teilnehmer*innen" +msgstr "Bestellungen oder Teilnehmer" #: pretix/plugins/sendmail/views.py:251 msgid "" "Send an email to every customer, or to every person a ticket has been " "purchased for, or a combination of both." msgstr "" -"Senden Sie eine E-Mail an alle Ticketkäufer*innen, alle Ticketinhaber*innen " +"Senden Sie eine E-Mail an alle Ticketkäufer, alle Ticketinhaber " "oder eine Kombination aus beiden Gruppen." #: pretix/plugins/sendmail/views.py:417 @@ -33088,23 +33104,23 @@ msgstr "SEPA-Lastschrift" #: pretix/plugins/stripe/payment.py:1232 msgid "Account Holder Name" -msgstr "Kontoinhaber*in" +msgstr "Kontoinhaber" #: pretix/plugins/stripe/payment.py:1237 msgid "Account Holder Street" -msgstr "Straße (Kontoinhaber*in)" +msgstr "Straße (Kontoinhaber)" #: pretix/plugins/stripe/payment.py:1249 msgid "Account Holder Postal Code" -msgstr "PLZ (Kontoinhaber*in)" +msgstr "PLZ (Kontoinhaber)" #: pretix/plugins/stripe/payment.py:1261 msgid "Account Holder City" -msgstr "Stadt (Kontoinhaber*in)" +msgstr "Stadt (Kontoinhaber)" #: pretix/plugins/stripe/payment.py:1273 msgid "Account Holder Country" -msgstr "Land (Kontoinhaber*in)" +msgstr "Land (Kontoinhaber)" #: pretix/plugins/stripe/payment.py:1317 msgid "Affirm via Stripe" @@ -34894,7 +34910,7 @@ msgstr[1] "Das Ticket wurde %(count)s-mal eingelöst." #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:166 msgid "No attendee name provided" -msgstr "Name der teilnehmenden Person nicht angegeben" +msgstr "Name des Teilnehmenrs nicht angegeben" #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:219 msgid "The image you previously uploaded" @@ -36627,12 +36643,12 @@ msgstr "" #: pretix/presale/templates/pretixpresale/organizers/customer_profiles.html:11 #: pretix/presale/views/customer.py:384 msgid "Attendee profiles" -msgstr "Teilnehmer*innen-Profile" +msgstr "Teilnehmer-Profile" #: pretix/presale/templates/pretixpresale/organizers/customer_profiles.html:37 msgid "You don’t have any attendee profiles in your account yet." msgstr "" -"In Ihrem Kundenkonto sind noch keine Teilnehmer*innen-Profile gespeichert." +"In Ihrem Kundenkonto sind noch keine Teilnehmer-Profile gespeichert." #: pretix/presale/templates/pretixpresale/organizers/customer_registration.html:7 msgid "Registration" @@ -38022,7 +38038,7 @@ msgstr "Kosovo" #~ "This plugin allows you to generate badges or name tags for your attendees." #~ msgstr "" #~ "Diese Erweiterung erlaubt, Namensschilder oder Badges für die " -#~ "Teilnehmer*innen zu erstellen." +#~ "Teilnehmer zu erstellen." #~ msgid "This plugin allows you to receive payments via PayPal" #~ msgstr "Dieses Plugin erlaubt, Zahlungen über PayPal anzunehmen" diff --git a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po index 8493188a5..9fdd34079 100644 --- a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po +++ b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po @@ -841,7 +841,7 @@ msgstr "Details der Bestellposition" #: pretix/base/datasync/sourcefields.py:129 msgid "Attendee details" -msgstr "Details Teilnehmer*in" +msgstr "Details Teilnehmer" #: pretix/base/datasync/sourcefields.py:130 pretix/base/exporters/answers.py:66 #: pretix/base/models/items.py:1766 pretix/control/navigation.py:172 @@ -903,13 +903,13 @@ msgstr "Veranstaltungs- oder Termininformationen" #: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:50 #: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html:36 msgid "Attendee name" -msgstr "Name Teilnehmer*in" +msgstr "Name Teilnehmer" #: pretix/base/datasync/sourcefields.py:187 #: pretix/base/datasync/sourcefields.py:604 #: pretix/base/datasync/sourcefields.py:628 msgid "Attendee" -msgstr "Teilnehmer*in" +msgstr "Teilnehmer" #: pretix/base/datasync/sourcefields.py:207 #: pretix/base/exporters/orderlist.py:612 pretix/base/forms/questions.py:687 @@ -920,11 +920,11 @@ msgstr "Teilnehmer*in" #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:172 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:175 msgid "Attendee email" -msgstr "E-Mail Teilnehmer*in" +msgstr "E-Mail Teilnehmer" #: pretix/base/datasync/sourcefields.py:219 msgid "Attendee or order email" -msgstr "E-Mail Teilnehmer*in oder Bestellung" +msgstr "E-Mail Teilnehmer oder Bestellung" #: pretix/base/datasync/sourcefields.py:232 #: pretix/base/exporters/orderlist.py:613 pretix/base/pdf.py:189 @@ -934,23 +934,23 @@ msgstr "E-Mail Teilnehmer*in oder Bestellung" #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:182 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:185 msgid "Attendee company" -msgstr "Teilnehmer*innen-Firma" +msgstr "Teilnehmer-Firma" #: pretix/base/datasync/sourcefields.py:241 msgid "Attendee address street" -msgstr "Teilnehmer*innen-Adresse: Straße" +msgstr "Teilnehmer-Adresse: Straße" #: pretix/base/datasync/sourcefields.py:250 msgid "Attendee address ZIP code" -msgstr "Teilnehmer*innen-Adresse: PLZ" +msgstr "Teilnehmer-Adresse: PLZ" #: pretix/base/datasync/sourcefields.py:259 msgid "Attendee address city" -msgstr "Teilnehmer*innen-Adresse: Stadt" +msgstr "Teilnehmer-Adresse: Stadt" #: pretix/base/datasync/sourcefields.py:268 msgid "Attendee address country" -msgstr "Teilnehmer*innen-Adresse: Land" +msgstr "Teilnehmer-Adresse: Land" #: pretix/base/datasync/sourcefields.py:279 #: pretix/base/exporters/orderlist.py:653 pretix/base/pdf.py:347 @@ -1227,7 +1227,7 @@ msgid "" "Download a ZIP file including all files that have been uploaded by your " "customers while creating an order." msgstr "" -"ZIP-Datei mit allen Dateien, die von Kund*innen im Bestellprozess als " +"ZIP-Datei mit allen Dateien, die von Kunden im Bestellprozess als " "Antwort auf eine Frage hochgeladen wurden." #: pretix/base/exporters/answers.py:76 pretix/base/exporters/orderlist.py:593 @@ -2196,8 +2196,8 @@ msgid "" "Download a text file with all email addresses collected either from buyers " "or from ticket holders." msgstr "" -"Textdatei mit allen E-Mail-Adressen, die von Käufer*innen und " -"Ticketinhaber*innen eingesammelt wurden." +"Textdatei mit allen E-Mail-Adressen, die von Käufer und " +"Ticketinhaber eingesammelt wurden." #: pretix/base/exporters/mail.py:76 pretix/plugins/reports/exporters.py:498 #: pretix/plugins/reports/exporters.py:681 @@ -4086,7 +4086,7 @@ msgstr "Bitte wähle einen gültigen Staat aus." #: pretix/base/modelimport_orders.py:359 pretix/control/forms/filter.py:683 msgid "Attendee email address" -msgstr "Teilnehmer*innen-E-Mail-Adresse" +msgstr "Teilnehmer-E-Mail-Adresse" #: pretix/base/modelimport_orders.py:375 pretix/base/modelimport_orders.py:386 #: pretix/base/modelimport_orders.py:397 pretix/base/modelimport_orders.py:408 @@ -4098,7 +4098,7 @@ msgstr "Teilnehmer*innen-E-Mail-Adresse" #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:193 #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:196 msgid "Attendee address" -msgstr "Teilnehmer*innen-Adresse" +msgstr "Teilnehmer-Adresse" #: pretix/base/modelimport_orders.py:445 msgid "State" @@ -7970,7 +7970,7 @@ msgstr "Musterfirma" #: pretix/base/pdf.py:194 msgid "Full attendee address" -msgstr "Volle Teilnehmer*innen-Adresse" +msgstr "Volle Teilnehmer-Adresse" #: pretix/base/pdf.py:195 msgid "" @@ -7988,23 +7988,23 @@ msgstr "" #: pretix/base/pdf.py:199 msgid "Attendee street" -msgstr "Teilnehmer*innen-Straße" +msgstr "Teilnehmer-Straße" #: pretix/base/pdf.py:204 msgid "Attendee ZIP code" -msgstr "Teilnehmer*innen-PLZ" +msgstr "Teilnehmer-PLZ" #: pretix/base/pdf.py:209 msgid "Attendee city" -msgstr "Teilnehmer*innen-Stadt" +msgstr "Teilnehmer-Stadt" #: pretix/base/pdf.py:214 msgid "Attendee state" -msgstr "Teilnehmer*innen-Bundesstaat" +msgstr "Teilnehmer-Bundesstaat" #: pretix/base/pdf.py:219 msgid "Attendee country" -msgstr "Teilnehmer*innen-Land" +msgstr "Teilnehmer-Land" #: pretix/base/pdf.py:231 msgid "Pseudonymization ID (lead scanning)" @@ -8297,7 +8297,7 @@ msgstr "Herr Mustermann" #: pretix/plugins/ticketoutputpdf/exporters.py:99 #, python-brace-format msgid "Attendee name: {part}" -msgstr "Teilnehmer*innenname: {part}" +msgstr "Teilnehmer: {part}" #: pretix/base/pdf.py:689 msgid "Invoice address name for salutation" @@ -8992,7 +8992,7 @@ msgstr "Veranstaltungsort: {location}" #, python-brace-format msgctxt "invoice" msgid "Attendee: {name}" -msgstr "Teilnehmer*in: {name}" +msgstr "Teilnehmer: {name}" #: pretix/base/services/invoices.py:278 pretix/plugins/reports/exporters.py:309 #, python-brace-format @@ -9820,7 +9820,7 @@ msgstr "" #: pretix/base/settings.py:347 msgid "Hide prices on attendee ticket page" -msgstr "Preise auf Teilnehmer*innen-Ticket-Seite verstecken" +msgstr "Preise auf Teilnehmer-Ticket-Seite verstecken" #: pretix/base/settings.py:348 msgid "" @@ -9830,13 +9830,13 @@ msgid "" "price." msgstr "" "Wenn eine Person mehrere Tickets erwirbt und E-Mails an alle " -"Teilnehmer*innen verschickt werden, wird mit dieser Option der Ticketpreis " -"auf der Ticket-Seite der einzelnen Teilnehmer*innen versteckt. Nur die " +"Teilnehmer verschickt werden, wird mit dieser Option der Ticketpreis " +"auf der Ticket-Seite der einzelnen Teilnehmer versteckt. Nur die " "Person, welche die Tickets kauft, sieht den Preis." #: pretix/base/settings.py:366 msgid "Ask for attendee names" -msgstr "Namen der Teilnehmer*innen erfragen" +msgstr "Namen der Teilnehmer erfragen" #: pretix/base/settings.py:367 msgid "Ask for a name for all personalized tickets." @@ -9845,11 +9845,11 @@ msgstr "" #: pretix/base/settings.py:376 msgid "Require attendee names" -msgstr "Namen der Teilnehmer*innen erfordern" +msgstr "Namen der Teilnehmer erfordern" #: pretix/base/settings.py:377 msgid "Require customers to fill in the names of all attendees." -msgstr "Erfordere die Eingabe aller Teilnehmer*innen-Namen." +msgstr "Erfordere die Eingabe aller Teilnehmer-Namen." #: pretix/base/settings.py:387 msgid "Ask for email addresses per ticket" @@ -9871,7 +9871,7 @@ msgstr "" "aktivierst, fragt das System zusätzlich nach einzelnen E-Mail-Adressen für " "jedes personalisierte Ticket in der Bestellung. Dies könnte z.B. nützlich " "sein, wenn du auch im Falle von Gruppenbestellungen individuelle Adressen " -"von jeder Teilnehmer*in benötigst. pretix sendet die Bestellbestätigung " +"von jedem Teilnehmer benötigst. pretix sendet die Bestellbestätigung " "standardmäßig nach wie vor nur an die primäre Adresse, dies kann jedoch in " "den E-Mail-Einstellungen angepasst werden." @@ -10359,8 +10359,8 @@ msgid "" "but no indication of missing payment will be visible on the ticket pages of " "attendees who did not buy the ticket themselves." msgstr "" -"Die Box mit Zahlungsinstruktionen wird Ticketkäufer*innen weiter angezeigt, " -"aber Teilnehmer*innen, die ihr Ticket nicht selbst gekauft haben, werden " +"Die Box mit Zahlungsinstruktionen wird Ticketkäufern weiter angezeigt, " +"aber Teilnehmer, die ihr Ticket nicht selbst gekauft haben, werden " "keine Anzeichen des fehlenden Zahlungseingangs sehen." #: pretix/base/settings.py:1027 @@ -10854,7 +10854,7 @@ msgstr "" #: pretix/base/settings.py:1638 msgid "Show number of check-ins to customer" -msgstr "Zeige Anzahl der Check-ins für Kund*innen an" +msgstr "Zeige Anzahl der Check-ins für Kunden an" #: pretix/base/settings.py:1639 msgid "" @@ -10865,12 +10865,12 @@ msgid "" "failed scans will not be counted, and the user will not see the different " "check-in lists." msgstr "" -"Wenn diese Option aktiv ist, können Kund*innen selbst sehen, wie oft sie die " +"Wenn diese Option aktiv ist, können Kunden selbst sehen, wie oft sie die " "Veranstaltung betreten haben. Das ist normalerweise nicht nötig, aber kann " "nützlich sein, wenn es Tickets gibt, die eine bestimmte Anzahl an Eintritten " -"erlauben, sodass Kund*innen die bisherige Nutzung des Tickets einsehen " +"erlauben, sodass Kunden die bisherige Nutzung des Tickets einsehen " "können. Ausgänge oder fehlgeschlagene Scans werden nicht angezeigt und die " -"Kund*innen sehen keine Aufschlüsselung verschiedener Check-in-Listen." +"Kunden sehen keine Aufschlüsselung verschiedener Check-in-Listen." #: pretix/base/settings.py:1652 msgid "Allow users to download tickets" @@ -11051,7 +11051,7 @@ msgstr "" #: pretix/base/settings.py:1815 pretix/base/settings.py:1824 msgid "Both the attendee and the person who ordered can make changes" msgstr "" -"Sowohl Besteller*in als auch Teilnehmer*innen können Änderungen vornehmen" +"Sowohl Besteller als auch Teilnehmer können Änderungen vornehmen" #: pretix/base/settings.py:1819 msgid "Allow customers to modify their information" @@ -11159,7 +11159,7 @@ msgstr "" #: pretix/base/settings.py:1925 msgid "Allow individual attendees to change their ticket" -msgstr "Erlaubt einzelnen Teilnehmer*innen ihr Ticket zu ändern" +msgstr "Erlaubt einzelnen Teilnehmer ihr Ticket zu ändern" #: pretix/base/settings.py:1926 msgid "" @@ -11171,7 +11171,7 @@ msgid "" msgstr "" "Standardmäßig kann nur die Person, welche die Tickets gekauft hat, " "Änderungen an der Bestellung vornehmen. Wenn diese Option aktiv ist, können " -"auch einzelne Teilnehmer*innen Änderungen vornehmen. Teilnehmer*innen können " +"auch einzelne Teilnehmer Änderungen vornehmen. Teilnehmer können " "jedoch immer nur Änderungen vornehmen, welche die Gesamtkosten der " "Bestellung nicht verändern. Solche Änderungen können nur vom Ticketkäufer " "vorgenommen werden." @@ -11355,7 +11355,7 @@ msgstr "Kontakt-E-Mail" #: pretix/base/settings.py:2161 pretix/control/forms/event.py:1824 msgid "We'll show this publicly to allow attendees to contact you." msgstr "" -"Wir werden diese Adresse veröffentlichen um Teilnehmer*innen zu ermöglichen, " +"Wir werden diese Adresse veröffentlichen um Teilnehmern zu ermöglichen, " "dich zu kontaktieren." #: pretix/base/settings.py:2169 pretix/control/forms/event.py:1816 @@ -11467,7 +11467,7 @@ msgid "" "people." msgstr "" "Du kannst dieses Feld benutzen um zusätzliche Informationen mit deinen " -"Teilnehmer*innen zu teilen, wie z.B. Anreise-Informationen oder den Link zu " +"Teilnehmer zu teilen, wie z.B. Anreise-Informationen oder den Link zu " "einer digitalen Veranstaltung. Wenn das Feld leer ist, fügen wir automatisch " "einen Link zum Ticketshop, die Einlass-Uhrzeit und den Veranstalter hier " "ein. Es sind keine Platzhalter mit sensiblen personenbezogenen Daten " @@ -11707,8 +11707,8 @@ msgstr "" "Diese Datei wird an die erste E-Mail angehängt, die wir beim Eingang einer " "neuen Bestellung verschicken. Sie kann daher mit den Textvorlagen " "\"Getätigte Bestellung\", \"Kostenlose Bestellung\" oder \"Erhaltene " -"Bestellung\" von oben auftreten. Sie wird ggf. sowohl an Besteller*innen als " -"auch Teilnehmer*innen verschickt. Nicht geeignet zum Versand nicht-" +"Bestellung\" von oben auftreten. Sie wird ggf. sowohl an Besteller als " +"auch Teilnehmer verschickt. Nicht geeignet zum Versand nicht-" "öffentlicher Informationen, da die Datei unabhängig davon verschickt wird, " "ob die Bestellung bezahlt oder freigegeben ist. Um zu vermeiden, dass diese " "wichtige E-Mail nicht ankommt, können nur PDF-Dateien mit maximal {size} MB " @@ -12929,7 +12929,7 @@ msgstr "" msgid "" "You cannot require specifying attendee names if you do not ask for them." msgstr "" -"Du kannst die Angabe von Teilnehmer*innennamen nur erfordern, wenn auch nach " +"Du kannst die Angabe von Teilnehmernamen nur erfordern, wenn auch nach " "Namen gefragt wird." #: pretix/base/settings.py:3992 @@ -14514,11 +14514,11 @@ msgstr "Ticket-Downloads" #: pretix/control/forms/event.py:1806 msgid "Your customers will be able to download their tickets in PDF format." msgstr "" -"Die Teilnehmer*innen werden ihre Tickets im PDF-Format herunterladen können." +"Die Teilnehmer werden ihre Tickets im PDF-Format herunterladen können." #: pretix/control/forms/event.py:1810 msgid "Require all attendees to fill in their names" -msgstr "Erfordere, dass alle Teilnehmer*innen ihre Namen ausfüllen" +msgstr "Erfordere, dass alle Teilnehmer ihre Namen ausfüllen" #: pretix/control/forms/event.py:1811 msgid "" @@ -14554,7 +14554,7 @@ msgid "" "then import your bank statements to process the payments within pretix, or " "mark them as paid manually." msgstr "" -"Deine Teilnehmer*innen werden angewiesen, das Geld direkt auf dein Bankkonto " +"Deine Teilnehmer werden angewiesen, das Geld direkt auf dein Bankkonto " "zu überweisen. Du kannst dann deinen Kontoauszug in pretix importieren, um " "Zahlungen zuzuweisen, oder die Bestellungen manuell als bezahlt markieren." @@ -14903,7 +14903,7 @@ msgstr "Check-in-Status" #: pretix/control/forms/filter.py:1913 #: pretix/plugins/checkinlists/exporters.py:108 msgid "All attendees" -msgstr "Alle Teilnehmer*innen" +msgstr "Alle Teilnehmer" #: pretix/control/forms/filter.py:1914 #: pretix/control/templates/pretixcontrol/checkin/index.html:183 @@ -15483,7 +15483,7 @@ msgid "" "people over 65. This ticket includes access to all parts of the event, " "except the VIP area." msgstr "" -"z.B. Dieses reduzierte Ticket ist erhältlich für Vollzeitstudent*innen, " +"z.B. Dieses reduzierte Ticket ist erhältlich für Vollzeitstudenten, " "Arbeitslose und Menschen über 65. Das Ticket enthält Zugang zu allen Teilen " "der Veranstaltung außer des VIP-Bereiches." @@ -17288,7 +17288,7 @@ msgstr "Eine individuelle E-Mail wurde verschickt." #: pretix/control/logdisplay.py:547 msgid "A custom email has been sent to an attendee." -msgstr "Eine individuelle E-Mail wurde an eine Teilnehmer*in verschickt." +msgstr "Eine individuelle E-Mail wurde an eine Teilnehmer verschickt." #: pretix/control/logdisplay.py:548 msgid "" @@ -18996,7 +18996,7 @@ msgstr "Terminal-ID" #: pretix/control/templates/pretixcontrol/boxoffice/payment.html:104 msgid "Card holder" -msgstr "Karteninhaber*in" +msgstr "Karteninhaber" #: pretix/control/templates/pretixcontrol/boxoffice/payment.html:108 msgid "Card expiration" @@ -19299,7 +19299,7 @@ msgstr "CSV" #: pretix/control/templates/pretixcontrol/checkin/index.html:73 msgid "No attendee record was found." -msgstr "Keine passenden Teilnehmer*innen gefunden." +msgstr "Keine passenden Teilnehmer gefunden." #: pretix/control/templates/pretixcontrol/checkin/index.html:91 #: pretix/control/templates/pretixcontrol/datasync/failed_jobs.html:19 @@ -21133,7 +21133,7 @@ msgid "" "provide ways for your attendees to contact you:" msgstr "" "Wenn irgendetwas schiefgeht oder unklar ist, empfehlen wir, dass du deinen " -"Teilnehmer*innen die Möglichkeit gibst, dich zu benachrichtigen:" +"Teilnehmer die Möglichkeit gibst, dich zu benachrichtigen:" #: pretix/control/templates/pretixcontrol/event/settings.html:7 #: pretix/control/templates/pretixcontrol/event/settings.html:13 @@ -22131,7 +22131,7 @@ msgid "" "Only purchases of such products will be considered \"attendees\" for most " "statistical purposes or within some plugins." msgstr "" -"Nur Käufe eines solchen Produkts werden als \"Teilnehmer*innen\" gewertet, " +"Nur Käufe eines solchen Produkts werden als \"Teilnehmer\" gewertet, " "z.B. in Statistiken oder in Funktionen von Erweiterungen." #: pretix/control/templates/pretixcontrol/item/create.html:39 @@ -22210,7 +22210,7 @@ msgid "" "The system will not ask for a name or other attendee details. This only " "affects system-provided fields, you can still add your own questions." msgstr "" -"Das System wird nicht nach einem Namen oder anderen Teilnehmer*innen-Daten " +"Das System wird nicht nach einem Namen oder anderen Teilnehmer-Daten " "fragen. Dies betrifft nur vom System bereitgestellte Felder, eigene Fragen " "können trotzdem hinzugefügt werden." @@ -22913,9 +22913,9 @@ msgid "" "ticket. If you provide food, one example might be to ask your users about " "dietary requirements." msgstr "" -"Fragen erlauben deinen Besucher*innen, zusätzliche Informationen zu ihrem " +"Fragen erlauben deinen Besucher, zusätzliche Informationen zu ihrem " "Ticket auszufüllen. Wenn deine Veranstaltung Verpflegung beinhaltet, " -"könntest du z.B. nach Allergien deiner Teilnehmer*innen fragen." +"könntest du z.B. nach Allergien deiner Teilnehmer fragen." #: pretix/control/templates/pretixcontrol/items/questions.html:14 msgid "Create a new question" @@ -23054,7 +23054,7 @@ msgstr "" "Um deine Produkte verfügbar zu machen, musst du Kontingente anlegen. " "Kontingente definieren, wie oft ein Produkt verkauft werden darf. Auf diese " "Art kannst du konfigurieren, ob deine Veranstaltung unbegrenzt viele " -"Teilnehmer*innen aufnehmen kann oder ob die Anzahl begrenzt ist. Du kannst " +"Teilnehmer aufnehmen kann oder ob die Anzahl begrenzt ist. Du kannst " "ein Produkt zu mehreren Kontingenten hinzufügen, um komplexere Anforderungen " "abzubilden, z.B. wenn du die Gesamtzahl der Tickets begrenzen willst, aber " "einen speziellen Ticket-Typ noch stärker begrenzen willst." @@ -24480,7 +24480,7 @@ msgstr "Sonstige Datenexporte" #: pretix/control/templates/pretixcontrol/orders/export.html:104 #: pretix/control/templates/pretixcontrol/organizers/export.html:104 msgid "Recommended for new users" -msgstr "Empfohlen für neue Benutzer*innen" +msgstr "Empfohlen für neue Benutzer" #: pretix/control/templates/pretixcontrol/orders/export_delete.html:4 #: pretix/control/templates/pretixcontrol/orders/export_delete.html:6 @@ -25446,7 +25446,7 @@ msgstr "Domains" #: pretix/control/templates/pretixcontrol/organizers/edit.html:319 msgid "This dialog is intended for advanced users." -msgstr "Dieser Dialog ist für fortgeschrittene Anwender*innen gedacht." +msgstr "Dieser Dialog ist für fortgeschrittene Anwender gedacht." #: pretix/control/templates/pretixcontrol/organizers/edit.html:320 msgid "" @@ -27724,7 +27724,7 @@ msgid "" "customers. This way, customers will not be able to discover the waiting list." msgstr "" "Entsprechend deiner Veranstaltungseinstellungen werden ausverkaufte Produkte " -"nicht angezeigt. Dies führt dazu, dass Kund*innen die Warteliste nicht " +"nicht angezeigt. Dies führt dazu, dass Kunden die Warteliste nicht " "finden können." #: pretix/control/templates/pretixcontrol/waitinglist/index.html:36 @@ -30090,7 +30090,7 @@ msgstr "PDF-Sammlungen" #: pretix/plugins/badges/exporters.py:423 msgid "Download all attendee badges as one large PDF for printing." -msgstr "Alle Teilnehmer*innen-Badges in einer großen PDF-Datei für den Druck." +msgstr "Alle Teilnehmer-Badges in einer großen PDF-Datei für den Druck." #: pretix/plugins/badges/exporters.py:444 #: pretix/plugins/ticketoutputpdf/exporters.py:80 @@ -30360,7 +30360,7 @@ msgstr "Anderes Bankkonto" #: pretix/plugins/banktransfer/payment.py:85 msgid "Name of account holder" -msgstr "Kontoinhaber*in" +msgstr "Kontoinhaber" #: pretix/plugins/banktransfer/payment.py:87 msgid "" @@ -30502,7 +30502,7 @@ msgstr "Bitte überweise den vollen Betrag auf das folgende Bankkonto:" #: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:32 #: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:35 msgid "Account holder" -msgstr "Kontoinhaber*in" +msgstr "Kontoinhaber" #: pretix/plugins/banktransfer/payment.py:303 #: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/checkout_payment_form.html:20 @@ -31113,7 +31113,7 @@ msgid "" "Download a spreadsheet with all attendees that are included in a check-in " "list." msgstr "" -"Tabelle (Excel oder CSV) mit allen Teilnehmer*innen, die in einer Check-in-" +"Tabelle (Excel oder CSV) mit allen Teilnehmer, die in einer Check-in-" "Liste zutrittsberechtigt sind." #: pretix/plugins/checkinlists/exporters.py:501 @@ -32290,7 +32290,7 @@ msgstr "Geplante E-Mails" #: pretix/plugins/sendmail/signals.py:122 msgid "Mass email was sent to customers or attendees." -msgstr "Rundmail wurde an Kunden oder Teilnehmer*innen verschickt." +msgstr "Rundmail wurde an Kunden oder Teilnehmer verschickt." #: pretix/plugins/sendmail/signals.py:123 msgid "Mass email was sent to waiting list entries." @@ -32322,7 +32322,7 @@ msgstr "Eine automatisierte E-Mail wurde an den Besteller verschickt" #: pretix/plugins/sendmail/signals.py:142 msgid "A scheduled email was sent to a ticket holder" -msgstr "Eine automatisierte E-Mail wurde an eine Teilnehmer*in verschickt." +msgstr "Eine automatisierte E-Mail wurde an einen Teilnehmer verschickt." #: pretix/plugins/sendmail/signals.py:143 msgid "An email rule was deleted" @@ -32355,7 +32355,7 @@ msgstr "Alle nicht eingecheckten Kunden" #: pretix/plugins/sendmail/templates/pretixplugins/sendmail/history_fragment_orders.html:23 msgid "Attendee contact addresses" -msgstr "Teilnehmer*innen-E-Mail-Adressen" +msgstr "Teilnehmer-E-Mail-Adressen" #: pretix/plugins/sendmail/templates/pretixplugins/sendmail/history_fragment_orders.html:25 msgid "All contact addresses" @@ -32515,14 +32515,14 @@ msgstr "" #: pretix/plugins/sendmail/views.py:250 msgid "Orders or attendees" -msgstr "Bestellungen oder Teilnehmer*innen" +msgstr "Bestellungen oder Teilnehmer" #: pretix/plugins/sendmail/views.py:251 msgid "" "Send an email to every customer, or to every person a ticket has been " "purchased for, or a combination of both." msgstr "" -"Sende eine E-Mail an alle Ticketkäufer*innen, alle Ticketinhaber*innen oder " +"Sende eine E-Mail an alle Ticketkäufer, alle Ticketinhaber oder " "eine Kombination aus beiden Gruppen." #: pretix/plugins/sendmail/views.py:417 @@ -33038,23 +33038,23 @@ msgstr "SEPA-Lastschrift" #: pretix/plugins/stripe/payment.py:1232 msgid "Account Holder Name" -msgstr "Kontoinhaber*in" +msgstr "Kontoinhaber" #: pretix/plugins/stripe/payment.py:1237 msgid "Account Holder Street" -msgstr "Straße (Kontoinhaber*in)" +msgstr "Straße (Kontoinhaber)" #: pretix/plugins/stripe/payment.py:1249 msgid "Account Holder Postal Code" -msgstr "PLZ (Kontoinhaber*in)" +msgstr "PLZ (Kontoinhaber)" #: pretix/plugins/stripe/payment.py:1261 msgid "Account Holder City" -msgstr "Stadt (Kontoinhaber*in)" +msgstr "Stadt (Kontoinhaber)" #: pretix/plugins/stripe/payment.py:1273 msgid "Account Holder Country" -msgstr "Land (Kontoinhaber*in)" +msgstr "Land (Kontoinhaber)" #: pretix/plugins/stripe/payment.py:1317 msgid "Affirm via Stripe" @@ -34837,7 +34837,7 @@ msgstr[1] "Das Ticket wurde %(count)s-mal eingelöst." #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:166 msgid "No attendee name provided" -msgstr "Name der teilnehmenden Person nicht angegeben" +msgstr "Name des Teilnehmenrs nicht angegeben" #: pretix/presale/templates/pretixpresale/event/fragment_cart.html:219 msgid "The image you previously uploaded" @@ -36568,12 +36568,12 @@ msgstr "Möchtest das folgende Profil wirklich aus deinem Kundenkonto löschen?" #: pretix/presale/templates/pretixpresale/organizers/customer_profiles.html:11 #: pretix/presale/views/customer.py:384 msgid "Attendee profiles" -msgstr "Teilnehmer*innen-Adresse" +msgstr "Teilnehmer-Adresse" #: pretix/presale/templates/pretixpresale/organizers/customer_profiles.html:37 msgid "You don’t have any attendee profiles in your account yet." msgstr "" -"In deinem Kundenkonto sind noch keine Teilnehmer*innen-Profile gespeichert." +"In deinem Kundenkonto sind noch keine Teilnehmer-Profile gespeichert." #: pretix/presale/templates/pretixpresale/organizers/customer_registration.html:7 msgid "Registration" @@ -37910,7 +37910,7 @@ msgstr "Kosovo" #~ "This plugin allows you to generate badges or name tags for your attendees." #~ msgstr "" #~ "Diese Erweiterung erlaubt, Namensschilder oder Badges für die " -#~ "Teilnehmer*innen zu erstellen." +#~ "Teilnehmer zu erstellen." #~ msgid "This plugin allows you to receive payments via PayPal" #~ msgstr "Dieses Plugin erlaubt, Zahlungen über PayPal anzunehmen" @@ -38488,7 +38488,7 @@ msgstr "Kosovo" #~ msgstr "Biete Ticket-Download bereits vor Bezahlung einer Bestellung an" #~ msgid "Attendee names" -#~ msgstr "Teilnehmer*innennamen" +#~ msgstr "Teilnehmername" #~ msgid "Enable output" #~ msgstr "Aktivieren" @@ -38765,7 +38765,7 @@ msgstr "Kosovo" #~ "If checked, users can cancel orders by themselves as long as they are not " #~ "yet paid." #~ msgstr "" -#~ "Wenn diese Option aktiviert ist, können Teilnehmer*innen selbstständig " +#~ "Wenn diese Option aktiviert ist, können Teilnehmer selbstständig " #~ "Bestellungen stornieren solange sie nicht bezahlt wurden." #~ msgid "Sales overview"