diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py
index 3f5e35d63f..422b55e95b 100644
--- a/src/pretix/base/models/orders.py
+++ b/src/pretix/base/models/orders.py
@@ -1271,6 +1271,21 @@ class QuestionAnswer(models.Model):
return self.file.name.split('.', 1)[-1]
def __str__(self):
+ return self.to_string(use_cached=True)
+
+ def to_string_i18n(self):
+ return self.to_string(use_cached=False)
+
+ def to_string(self, use_cached=True):
+ """
+ Render this answer as a string.
+
+ :param use_cached: If ``True`` (default), choice and multiple choice questions will show their cached
+ value, i.e. the value of the selected options at the time of saving and in the language
+ the answer was saved in. If ``False``, the values will instead be loaded from the
+ database, yielding current and translated values of the options. However, additional database
+ queries might be required.
+ """
if self.question.type == Question.TYPE_BOOLEAN and self.answer == "True":
return str(_("Yes"))
elif self.question.type == Question.TYPE_BOOLEAN and self.answer == "False":
@@ -1305,6 +1320,8 @@ class QuestionAnswer(models.Model):
return PhoneNumber.from_string(self.answer).as_international
except NumberParseException:
return self.answer
+ elif self.question.type in (Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE) and self.answer and not use_cached:
+ return ", ".join(str(o.answer) for o in self.options.all())
else:
return self.answer
diff --git a/src/pretix/base/pdf.py b/src/pretix/base/pdf.py
index 748dd3d6a4..7224066a1f 100644
--- a/src/pretix/base/pdf.py
+++ b/src/pretix/base/pdf.py
@@ -567,7 +567,7 @@ def variables_from_questions(sender, *args, **kwargs):
if not a:
return ""
else:
- return str(a)
+ return a.to_string_i18n()
d = {}
for q in sender.questions.all():
diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py
index 1039c2e6ba..86b30a7339 100644
--- a/src/pretix/base/services/invoices.py
+++ b/src/pretix/base/services/invoices.py
@@ -199,7 +199,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
positions = list(
invoice.order.positions.select_related('addon_to', 'item', 'tax_rule', 'subevent', 'variation').annotate(
addon_c=Count('addons')
- ).prefetch_related('answers', 'answers__question').order_by('positionid', 'id')
+ ).prefetch_related('answers', 'answers__options', 'answers__question').order_by('positionid', 'id')
)
reverse_charge = False
@@ -247,7 +247,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
desc += "
{}{} {}".format(
answ.question.question,
"" if str(answ.question.question).endswith("?") else ":",
- str(answ)
+ answ.to_string_i18n()
)
if invoice.event.has_subevents:
diff --git a/src/pretix/control/templates/pretixcontrol/order/index.html b/src/pretix/control/templates/pretixcontrol/order/index.html
index 02ae8aa870..b6b6d89b00 100644
--- a/src/pretix/control/templates/pretixcontrol/order/index.html
+++ b/src/pretix/control/templates/pretixcontrol/order/index.html
@@ -589,9 +589,9 @@
{% endif %}
{% elif q.type == "M" %}
- {{ q.answer|rich_text_snippet }}
+ {{ q.answer.to_string_i18n|rich_text_snippet }}
{% else %}
- {{ q.answer|linebreaksbr }}
+ {{ q.answer.to_string_i18n|linebreaksbr }}
{% endif %}
{% else %}
{% trans "not answered" %}