From b7da9bb6c14bc566dbe616d8892eb35156e324df Mon Sep 17 00:00:00 2001
From: Mira Weller
Date: Wed, 17 Jun 2026 12:16:20 +0200
Subject: [PATCH] handle hidden questions
---
.../0299_questionnaire_questionnairechild.py | 63 +++++++++++--------
src/pretix/base/models/items.py | 2 +
.../pretixcontrol/items/questionnaires.html | 1 +
.../js/ui/questionnaires/App.vue | 4 +-
.../js/ui/questionnaires/model.ts | 5 +-
5 files changed, 46 insertions(+), 29 deletions(-)
diff --git a/src/pretix/base/migrations/0299_questionnaire_questionnairechild.py b/src/pretix/base/migrations/0299_questionnaire_questionnairechild.py
index add93eae48..f8f9b41065 100644
--- a/src/pretix/base/migrations/0299_questionnaire_questionnairechild.py
+++ b/src/pretix/base/migrations/0299_questionnaire_questionnairechild.py
@@ -49,39 +49,21 @@ def migrate_questions_forward(apps, schema_editor):
QuestionnaireChild = apps.get_model("pretixbase", "QuestionnaireChild")
EventSettingsStore = apps.get_model('pretixbase', 'Event_SettingsStore')
- for event in Event.objects.iterator():
- # get relevant settings
- settings = {
- setting.key: setting.value for setting in EventSettingsStore.objects.filter(object_id=event.id, key__in=(
- 'system_question_order', 'attendee_names_asked', 'attendee_names_required', 'attendee_emails_asked', 'attendee_emails_required',
- 'attendee_company_asked', 'attendee_company_required', 'attendee_addresses_asked', 'attendee_addresses_required',
- ))
- }
-
- # get all questions (user-defined and system provided), along with the products for which they're asked
- questions = event.questions.all()
- children = sorted(chain((
- (item, q.position, q.id, q)
- for q in questions
- for item in q.items.values_list('id', 'internal_name', 'name')
- ), (
- (item, q.position, None, q)
- for q in get_fake_questions(settings)
- for item in event.items.filter(personalized=True).values_list('id', 'internal_name', 'name')
- )), key=lambda t: (t[0], t[1], t[2]))
-
+ def create_grouped_item_questionnaires(event, children, label_prefix, questionnaire_type):
# group by item, creating a unique questionnaire per item
- item_questionnaires = (([t[3] for t in children], item_id) for item_id, children in groupby(children, key=lambda t: t[0]))
+ item_questionnaires = (([t[3] for t in children], item_id) for item_id, children in
+ groupby(children, key=lambda t: t[0]))
# group again, merging all questionnaires with identical children
- merged_questionnaires = groupby(sorted(item_questionnaires, key=lambda t: [q.id for q in t[0]]), key=lambda t: t[0])
+ merged_questionnaires = groupby(sorted(item_questionnaires, key=lambda t: [q.id for q in t[0]]),
+ key=lambda t: t[0])
for children, iterator in merged_questionnaires:
items = [item for _c, item in iterator]
# create questionnaires and children
questionnaire = Questionnaire.objects.create(
- event=event, type='PS', position=0, all_sales_channels=True,
- internal_name=', '.join(str(iname or name) for (id, iname, name) in items)
+ event=event, type=questionnaire_type, position=0, all_sales_channels=True,
+ internal_name=label_prefix + ', '.join(str(iname or name) for (id, iname, name) in items)
)
questionnaire.items.set([id for (id, iname, name) in items])
deps = {}
@@ -106,6 +88,37 @@ def migrate_questions_forward(apps, schema_editor):
dependency_values=child.dependency_values,
)
+ for event in Event.objects.iterator():
+ # get relevant settings
+ settings = {
+ setting.key: setting.value for setting in EventSettingsStore.objects.filter(object_id=event.id, key__in=(
+ 'system_question_order', 'attendee_names_asked', 'attendee_names_required', 'attendee_emails_asked', 'attendee_emails_required',
+ 'attendee_company_asked', 'attendee_company_required', 'attendee_addresses_asked', 'attendee_addresses_required',
+ ))
+ }
+
+ # get all questions (user-defined and system provided), along with the products for which they're asked
+ questions = event.questions.all()
+ children = sorted(chain((
+ (item, q.position, q.id, q)
+ for q in questions.filter(hidden=False)
+ for item in q.items.values_list('id', 'internal_name', 'name')
+ ), (
+ (item, q.position, 0, q)
+ for q in get_fake_questions(settings)
+ for item in event.items.filter(personalized=True).values_list('id', 'internal_name', 'name')
+ )), key=lambda t: (t[0], t[1], t[2]))
+
+ create_grouped_item_questionnaires(event, children, '', 'PS')
+
+ children = sorted(chain((
+ (item, q.position, q.id, q)
+ for q in questions.filter(hidden=True)
+ for item in q.items.values_list('id', 'internal_name', 'name')
+ )), key=lambda t: (t[0], t[1], t[2]))
+
+ create_grouped_item_questionnaires(event, children, 'Hidden questions for ', 'PH')
+
def migrate_questions_backward(apps, schema_editor):
pass # as long as we don't delete the old columns, this is a no op. after that, it gets complicated...
diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py
index 328c7c93a7..4a61bede56 100644
--- a/src/pretix/base/models/items.py
+++ b/src/pretix/base/models/items.py
@@ -1997,11 +1997,13 @@ class Questionnaire(LoggedModel):
TYPE_ORDER_POSITION_SALE = "PS"
TYPE_ORDER_POSITION_ATTENDEE_ONLY = "PA"
TYPE_ORDER_POSITION_CHECKIN = "PC"
+ TYPE_ORDER_POSITION_HIDDEN = "PH"
TYPE_CHOICES = (
(TYPE_ORDER_SALE, _("Order-wide, before purchase")),
(TYPE_ORDER_POSITION_SALE, _("Per product, before purchase")),
(TYPE_ORDER_POSITION_ATTENDEE_ONLY, _("Per product, via attendee link")),
(TYPE_ORDER_POSITION_CHECKIN, _("Per product, at check-in")),
+ (TYPE_ORDER_POSITION_HIDDEN, _("Per product, hidden")),
)
event = models.ForeignKey(
Event,
diff --git a/src/pretix/control/templates/pretixcontrol/items/questionnaires.html b/src/pretix/control/templates/pretixcontrol/items/questionnaires.html
index 1937d172f6..0d4cb663a3 100644
--- a/src/pretix/control/templates/pretixcontrol/items/questionnaires.html
+++ b/src/pretix/control/templates/pretixcontrol/items/questionnaires.html
@@ -24,6 +24,7 @@
{{ request.event.settings.locales|json_script:"event_locales" }}
+ {{ questionnaire_type_choices|json_script:"questionnaire_type_choices" }}
diff --git a/src/pretix/static/pretixcontrol/js/ui/questionnaires/App.vue b/src/pretix/static/pretixcontrol/js/ui/questionnaires/App.vue
index f70a60d350..51290cecef 100644
--- a/src/pretix/static/pretixcontrol/js/ui/questionnaires/App.vue
+++ b/src/pretix/static/pretixcontrol/js/ui/questionnaires/App.vue
@@ -20,8 +20,8 @@ export default {
addQuestionnaire() {
questionnaires.value.push({
all_sales_channels: false, children: [], limit_sales_channels: [], position: 0,
- items: [], internal_name: "Unnamed questionnaire",
- _new_id: Date.now()
+ items: [], internal_name: "Unnamed questionnaire", type: "PC",
+ _new_id: Date.now(),
});
},
saveData() {
diff --git a/src/pretix/static/pretixcontrol/js/ui/questionnaires/model.ts b/src/pretix/static/pretixcontrol/js/ui/questionnaires/model.ts
index f88296e82e..43df7e3272 100644
--- a/src/pretix/static/pretixcontrol/js/ui/questionnaires/model.ts
+++ b/src/pretix/static/pretixcontrol/js/ui/questionnaires/model.ts
@@ -17,12 +17,12 @@ export type Datafield = {
//items: number[],
options: any[],
// position: number,
- ask_during_checkin: boolean,
+ //ask_during_checkin: boolean,
show_during_checkin: boolean,
identifier: string,
// dependency_question: string | null,
// dependency_values []
- hidden: boolean,
+ // hidden: boolean,
// dependency_value null
print_on_invoice: boolean,
// help_text: I18nString,
@@ -40,6 +40,7 @@ export type Datafield = {
export type Questionnaire = {
id: number,
internal_name: string,
+ type: string,
items: number[],
position: number,
all_sales_channels: boolean,