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,