diff --git a/src/pretix/base/forms/questions.py b/src/pretix/base/forms/questions.py index 578dcb80c..407321053 100644 --- a/src/pretix/base/forms/questions.py +++ b/src/pretix/base/forms/questions.py @@ -650,7 +650,7 @@ class BaseQuestionsForm(forms.Form): orderpos = self.orderpos = kwargs.pop('orderpos', None) pos = cartpos or orderpos item = pos.item - questions = pos.item.questions_to_ask + questionnaires = pos.item.relevant_questionnaires event = kwargs.pop('event') self.all_optional = kwargs.pop('all_optional', False) self.attendee_addresses_required = event.settings.attendee_addresses_required and not self.all_optional @@ -696,38 +696,13 @@ class BaseQuestionsForm(forms.Form): validators=([MaxDateTimeValidator(max_date)] if max_date else []) + [MinDateTimeValidator(min_date)] ) - add_fields = {} - - if item.ask_attendee_data and event.settings.attendee_names_asked: - add_fields['attendee_name_parts'] = self.build_system_question_field(request, event, pos, 'attendee_name_parts') - if item.ask_attendee_data and event.settings.attendee_emails_asked: - add_fields['attendee_email'] = self.build_system_question_field(request, event, pos, 'attendee_email') - if item.ask_attendee_data and event.settings.attendee_company_asked: - add_fields['company'] = self.build_system_question_field(request, event, pos, 'company') - - if item.ask_attendee_data and event.settings.attendee_addresses_asked: - add_fields['street'] = self.build_system_question_field(request, event, pos, 'street') - add_fields['zipcode'] = self.build_system_question_field(request, event, pos, 'zipcode') - add_fields['city'] = self.build_system_question_field(request, event, pos, 'city') - add_fields['country'] = self.build_system_question_field(request, event, pos, 'country') - add_fields['state'] = self.build_system_question_field(request, event, pos, 'state') - - field_positions = list( - [ - (n, event.settings.system_question_order.get(n if n != 'state' else 'country', 0)) - for n in add_fields.keys() - ] - ) - - for q in questions: - field = self.build_user_question_field(request, event, pos, q) - - add_fields['question_%s' % q.id] = field - field_positions.append(('question_%s' % q.id, q.position)) - - field_positions.sort(key=lambda e: e[1]) - for fname, p in field_positions: - self.fields[fname] = add_fields[fname] + for questionnaire in questionnaires: + for child in getattr(questionnaire, 'childlist', questionnaire.children.all()): + if child.user_question: + q = child.user_question + self.fields['question_%s' % q.id] = self.build_user_question_field(request, event, pos, q) + elif child.system_question: + self.fields[child.system_question] = self.build_system_question_field(request, event, pos, child) responses = question_form_fields.send(sender=event, position=pos) data = pos.meta_info_data @@ -752,20 +727,23 @@ class BaseQuestionsForm(forms.Form): else: v.widget.attrs['autocomplete'] = 'section-{} '.format(self.prefix) + autocomplete - def build_system_question_field(self, request, event, pos, field_name): + def build_system_question_field(self, request, event, pos, qc): + field_name = qc.system_question if field_name == 'attendee_name_parts': return NamePartsFormField( max_length=255, - required=event.settings.attendee_names_required and not self.all_optional, + required=qc.required and not self.all_optional, scheme=event.settings.name_scheme, titles=event.settings.name_scheme_titles, - label=_('Attendee name'), + label=qc.label, + help_text=qc.help_text, initial=pos.attendee_name_parts, ) if field_name == 'attendee_email': return forms.EmailField( - required=event.settings.attendee_emails_required and not self.all_optional, - label=_('Attendee email'), + required=qc.required and not self.all_optional, + label=qc.label, + help_text=qc.help_text, initial=pos.attendee_email, widget=forms.EmailInput( attrs={ @@ -775,16 +753,18 @@ class BaseQuestionsForm(forms.Form): ) if field_name == 'company': return forms.CharField( - required=event.settings.attendee_company_required and not self.all_optional, - label=_('Company'), + required=qc.required and not self.all_optional, + label=qc.label, + help_text=qc.help_text, max_length=255, initial=pos.company, ) if field_name == 'street': return forms.CharField( - required=self.attendee_addresses_required, - label=_('Address'), + required=qc.required and not self.all_optional, + label=qc.label, + help_text=qc.help_text, widget=forms.Textarea(attrs={ 'rows': 2, 'placeholder': _('Street and Number'), @@ -796,7 +776,8 @@ class BaseQuestionsForm(forms.Form): return forms.CharField( required=False, max_length=30, - label=_('ZIP code'), + label=qc.label, + help_text=qc.help_text, initial=pos.zipcode, widget=forms.TextInput(attrs={ 'autocomplete': 'postal-code', @@ -805,7 +786,8 @@ class BaseQuestionsForm(forms.Form): if field_name == 'city': return forms.CharField( required=False, - label=_('City'), + label=qc.label, + help_text=qc.help_text, max_length=255, initial=pos.city, widget=forms.TextInput(attrs={ @@ -817,8 +799,9 @@ class BaseQuestionsForm(forms.Form): return CountryField( countries=CachedCountries ).formfield( - required=self.attendee_addresses_required, - label=_('Country'), + required=qc.required and not self.all_optional, + label=qc.label, + help_text=qc.help_text, initial=country, widget=forms.Select(attrs={ 'autocomplete': 'country', @@ -845,7 +828,8 @@ class BaseQuestionsForm(forms.Form): del self.data[fprefix + 'state'] field = forms.ChoiceField( - label=pgettext_lazy('address', 'State'), + label=qc.label, + help_text=qc.help_text, required=False, choices=c, initial=state, diff --git a/src/pretix/base/models/__init__.py b/src/pretix/base/models/__init__.py index 3657cfb4c..5b965574e 100644 --- a/src/pretix/base/models/__init__.py +++ b/src/pretix/base/models/__init__.py @@ -37,7 +37,7 @@ from .invoices import Invoice, InvoiceLine, invoice_filename from .items import ( Item, ItemAddOn, ItemBundle, ItemCategory, ItemMetaProperty, ItemMetaValue, ItemProgramTime, ItemVariation, ItemVariationMetaValue, Question, - QuestionOption, Quota, SubEventItem, SubEventItemVariation, + QuestionOption, Questionnaire, QuestionnaireChild, Quota, SubEventItem, SubEventItemVariation, itempicture_upload_to, ) from .log import LogEntry diff --git a/src/pretix/base/views/mixins.py b/src/pretix/base/views/mixins.py index f85228d8a..56adb27ee 100644 --- a/src/pretix/base/views/mixins.py +++ b/src/pretix/base/views/mixins.py @@ -36,7 +36,7 @@ from pretix.base.forms.questions import ( ) from pretix.base.models import ( CartPosition, InvoiceAddress, OrderPosition, Question, QuestionAnswer, - QuestionOption, + QuestionOption, QuestionnaireChild, ) from pretix.base.models.customers import AttendeeProfile from pretix.presale.signals import contact_form_fields_overrides @@ -307,27 +307,34 @@ class OrderQuestionsViewMixin(BaseQuestionsViewMixin): @cached_property def positions(self): - qqs = self.request.event.questions.all() + qqs = self.request.event.questionnaires.all() if self.only_user_visible: - qqs = qqs.filter(ask_during_checkin=False, hidden=False) + qqs = qqs.filter(type='PS') + else: + qqs = qqs.filter(type__startswith='P') return list(self.order.positions.select_related( 'item', 'variation' ).prefetch_related( Prefetch('answers', QuestionAnswer.objects.prefetch_related('options'), to_attr='answerlist'), - Prefetch('item__questions', + Prefetch('item__questionnaires', qqs.prefetch_related( - Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch( - # This prefetch statement is utter bullshit, but it actually prevents Django from doing - # a lot of queries since ModelChoiceIterator stops trying to be clever once we have - # a prefetch lookup on this query... - 'question', - Question.objects.none(), - to_attr='dummy' - ))) - ).select_related('dependency_question'), - to_attr='questions_to_ask') + Prefetch('children', QuestionnaireChild.objects.prefetch_related( + Prefetch('user_question', Question.objects.prefetch_related( + Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch( + # This prefetch statement is utter bullshit, but it actually prevents Django from doing + # a lot of queries since ModelChoiceIterator stops trying to be clever once we have + # a prefetch lookup on this query... + 'question', + Question.objects.none(), + to_attr='dummy' + ))) + )) + ), + to_attr='childlist') + ), + to_attr='relevant_questionnaires') )) @cached_property diff --git a/src/pretix/presale/views/__init__.py b/src/pretix/presale/views/__init__.py index 46822dd46..b987f4950 100644 --- a/src/pretix/presale/views/__init__.py +++ b/src/pretix/presale/views/__init__.py @@ -54,6 +54,7 @@ from pretix.base.models import ( CartPosition, Customer, InvoiceAddress, ItemAddOn, OrderFee, Question, QuestionAnswer, QuestionOption, TaxRule, ) +from pretix.base.models.items import QuestionnaireChild from pretix.base.services.cart import get_fees from pretix.base.services.pricing import apply_rounding from pretix.base.templatetags.money import money_filter @@ -397,8 +398,8 @@ def get_cart(request): if not cart_id: request._cart_cache = CartPosition.objects.none() else: - qqs = request.event.questions.all() - qqs = qqs.filter(ask_during_checkin=False, hidden=False) + qqs = request.event.questionnaires.all() + qqs = qqs.filter(type='PS') request._cart_cache = CartPosition.objects.filter( cart_id=cart_id, event=request.event ).annotate( @@ -419,18 +420,23 @@ def get_cart(request): Prefetch('answers', QuestionAnswer.objects.prefetch_related('options'), to_attr='answerlist'), - Prefetch('item__questions', + Prefetch('item__questionnaires', qqs.prefetch_related( - Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch( - # This prefetch statement is utter bullshit, but it actually prevents Django from doing - # a lot of queries since ModelChoiceIterator stops trying to be clever once we have - # a prefetch lookup on this query... - 'question', - Question.objects.none(), - to_attr='dummy' - ))) - ).select_related('dependency_question'), - to_attr='questions_to_ask') + Prefetch('children', QuestionnaireChild.objects.prefetch_related( + Prefetch('user_question', Question.objects.prefetch_related( + Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch( + # This prefetch statement is utter bullshit, but it actually prevents Django from doing + # a lot of queries since ModelChoiceIterator stops trying to be clever once we have + # a prefetch lookup on this query... + 'question', + Question.objects.none(), + to_attr='dummy' + ))) + )) + ), + to_attr='childlist') + ), + to_attr='relevant_questionnaires') ) by_id = {cp.pk: cp for cp in request._cart_cache} for cp in request._cart_cache: